Skip to content

Commit 215a176

Browse files
feat: 添加用户信息获取功能并更新帮助文档
1 parent 3be8796 commit 215a176

File tree

7 files changed

+181
-13
lines changed

7 files changed

+181
-13
lines changed

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,44 +13,52 @@ GitHubView 是一个用于查看 GitHub 上的信息的命令行工具。目前
1313
| `about` | 查看关于此应用的信息 | / | / |
1414
| `contribute` | 获取指定仓库的前 N 个贡献者信息 (贡献者排行榜) | `ctb 所有者 仓库名 前N个` | `ctb` |
1515
| `labels` | 获取指定仓库的所有标签 | `label 所有者 仓库名` | `label` |
16+
| `user` | 获取指定用户的信息 | `user 用户名` | / |
1617

1718
### 详细介绍
1819
#### help
1920
> 别名: `--help` `-h`
2021
2122
<div style="display: flex; gap: 15px;">
2223
<img src="https://duckduckstudio.github.io/GitHubView/images/README/help.png" alt="显示所有命令的表格" style="height: 75%; width: 75%;">
23-
<p>显示关于此程序的帮助信息。</p>
24+
<p><strong>显示关于此程序的帮助信息。</strong></p>
2425
</div>
2526

2627
#### version
2728
> 别名: `v` `--version` `ver` `-v`
2829
2930
<div style="display: flex; gap: 15px;">
3031
<img src="https://duckduckstudio.github.io/GitHubView/images/README/version.png" alt="显示版本信息的表格" style="height: 75%; width: 75%;">
31-
<p>显示关于此程序的版本信息。<br>在部分终端可以点击内容跳转到详细链接。</p>
32+
<p><strong>显示关于此程序的版本信息。</strong><br>在部分终端可以点击内容跳转到详细链接。</p>
3233
</div>
3334

3435
#### about
3536
<div style="display: flex; gap: 15px;">
3637
<img src="https://duckduckstudio.github.io/GitHubView/images/README/about.png" alt="显示详细信息的表格" style="height: 75%; width: 75%;">
37-
<p>显示关于此程序的详细信息。<br>在部分终端可以点击内容跳转到详细链接。</p>
38+
<p><strong>显示关于此程序的详细信息。</strong><br>在部分终端可以点击内容跳转到详细链接。</p>
3839
</div>
3940

4041
#### contribute
4142
> 别名: `ctb`
4243
4344
<div style="display: flex; gap: 15px;">
4445
<img src="https://duckduckstudio.github.io/GitHubView/images/README/contribute.png" alt="使用示例" style="height: 75%; width: 75%;">
45-
<p>获取指定仓库的前 N 个贡献者信息。<br>换句话说就是获取仓库的贡献数/者排行榜。<br>点击用户名可跳转用户界面。</p>
46+
<p><strong>获取指定仓库的前 N 个贡献者信息。</strong><br>换句话说就是获取仓库的贡献数/者排行榜。<br>点击用户名可跳转用户界面。</p>
4647
</div>
4748

4849
#### labels
4950
> 别名: `label`
5051
5152
<div style="display: flex; gap: 15px;">
5253
<img src="https://duckduckstudio.github.io/GitHubView/images/README/labels.png" alt="使用示例" style="height: 75%; width: 75%;">
53-
<p>获取指定仓库的所有标签。<br><del>相当于<code><a href="https://github.com/DuckDuckStudio/GitHub-Labels-Manager" target="_blank">glm</a> get</code>的表格版。</del></p>
54+
<p><strong>获取指定仓库的所有标签。</strong><br><del>相当于<code><a href="https://github.com/DuckDuckStudio/GitHub-Labels-Manager" target="_blank">glm</a> get</code>的表格版。</del></p>
55+
</div>
56+
57+
#### user
58+
59+
<div style="display: flex; gap: 15px;">
60+
<img src="https://duckduckstudio.github.io/GitHubView/images/README/user.png" alt="使用示例" style="height: 75%; width: 75%;">
61+
<p><strong>获取指定用户的信息。</strong><br>在部分终端可以点击内容跳转到详细链接。<br>如果某项属性为空则不会显示那项属性。</p>
5462
</div>
5563

5664
## 项目状态

docs/images/README/help.png

164 KB
Loading

docs/images/README/user.png

109 KB
Loading

ghv/Command/Help.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ public static void Show()
88
{
99
var table = new Table();
1010
table.AddColumn("命令");
11-
table.AddColumn("描述");
11+
table.AddColumn("作用");
12+
table.AddColumn("参数");
13+
table.AddColumn("别名");
1214

13-
table.AddRow("help", "显示帮助信息");
14-
table.AddRow("version", "显示应用程序版本信息");
15-
table.AddRow("about", "显示有关此应用的相关信息");
16-
table.AddRow("contribute", "获取指定仓库的前 N 个贡献者信息");
17-
table.AddRow("labels", "获取指定仓库的所有标签");
15+
// [] 需要转义
16+
table.AddRow("help", "显示帮助信息", "/", "--help -h");
17+
table.AddRow("version", "显示应用程序版本信息", "/", "v --version ver -v");
18+
table.AddRow("about", "显示有关此应用的相关信息", "/", "/");
19+
table.AddRow("contribute", "获取指定仓库的前 N 个贡献者信息", "ctb [[所有者]] [[仓库名]] [[前N个]]", "ctb");
20+
table.AddRow("labels", "获取指定仓库的所有标签", "label [[所有者]] [[仓库名]]", "label");
21+
table.AddRow("user", "获取指定用户的信息", "user [[用户名]]", "/");
1822

1923
AnsiConsole.Write(table);
2024
}

ghv/Command/User.cs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
using Spectre.Console;
2+
using System.Text.Json.Nodes;
3+
using System.Text.RegularExpressions;
4+
5+
namespace ghv.Command
6+
{
7+
internal class User
8+
{
9+
public static async Task ExecuteAsync(string username)
10+
{
11+
if (string.IsNullOrWhiteSpace(username))
12+
{
13+
username = GetUserInput("请输入用户名:", ValidateUsername);
14+
}
15+
16+
bool userExists = await ValidateUserExists(username);
17+
if (!userExists)
18+
{
19+
AnsiConsole.Markup("[red]该用户不存在,请检查输入的用户名是否正确。[/]\n");
20+
return;
21+
}
22+
23+
await FetchAndDisplayUserInfo(username);
24+
}
25+
26+
private static string GetUserInput(string prompt, Func<string, bool> validator)
27+
{
28+
string input;
29+
do
30+
{
31+
AnsiConsole.Markup("[cyan]? [/]" + prompt + " ");
32+
input = (Console.ReadLine() ?? string.Empty).Trim();
33+
34+
if (!validator(input))
35+
{
36+
AnsiConsole.Markup("[red]输入无效,请重新输入。[/]\n");
37+
}
38+
}
39+
while (string.IsNullOrWhiteSpace(input) || !validator(input));
40+
41+
return input;
42+
}
43+
44+
private static async Task<bool> ValidateUserExists(string username)
45+
{
46+
using HttpClient client = new();
47+
string url = $"https://api.github.com/users/{username}";
48+
client.DefaultRequestHeaders.Add("User-Agent", "CSharpApp");
49+
HttpResponseMessage response = await client.GetAsync(url);
50+
return response.IsSuccessStatusCode;
51+
}
52+
53+
private static async Task FetchAndDisplayUserInfo(string username)
54+
{
55+
using HttpClient client = new();
56+
string url = $"https://api.github.com/users/{username}";
57+
client.DefaultRequestHeaders.Add("User-Agent", "CSharpApp");
58+
HttpResponseMessage response = await client.GetAsync(url);
59+
60+
if (response.IsSuccessStatusCode)
61+
{
62+
string jsonResponse = await response.Content.ReadAsStringAsync();
63+
JsonNode? jsonNode = JsonNode.Parse(jsonResponse);
64+
if (jsonNode == null)
65+
{
66+
AnsiConsole.Markup("[red]无法解析用户数据。[/]\n");
67+
return;
68+
}
69+
70+
var table = new Table();
71+
table.Border(TableBorder.Rounded);
72+
table.BorderColor(Color.Grey);
73+
table.AddColumn(new TableColumn("属性").Centered());
74+
table.AddColumn(new TableColumn("值").Centered());
75+
76+
var propertiesToDisplay = new List<string> { "login", "name", "company", "blog", "location", "email", "bio", "twitter_username" };
77+
var propertyTranslations = new Dictionary<string, string>
78+
{
79+
{ "login", "登录名" },
80+
{ "name", "姓名" },
81+
{ "company", "公司" },
82+
{ "blog", "博客" },
83+
{ "location", "位置" },
84+
{ "email", "电子邮件" },
85+
{ "bio", "简介" },
86+
{ "twitter_username", "Twitter" }
87+
};
88+
89+
string? login = jsonNode["login"]?.ToString();
90+
string? name = jsonNode["name"]?.ToString();
91+
if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(login))
92+
{
93+
string combinedName = $"[link=https://github.com/{login}]{name} ([grey]{login}[/])[/]";
94+
table.AddRow("姓名", combinedName).Border(TableBorder.Square);
95+
}
96+
97+
foreach (var property in jsonNode.AsObject())
98+
{
99+
if (propertiesToDisplay.Contains(property.Key) && property.Key != "login" && property.Key != "name")
100+
{
101+
string propertyName = propertyTranslations.ContainsKey(property.Key) ? propertyTranslations[property.Key] : property.Key;
102+
string propertyValue = Markup.Escape(property.Value?.ToString() ?? string.Empty);
103+
104+
if (!string.IsNullOrWhiteSpace(propertyValue))
105+
{
106+
if (property.Key == "twitter_username")
107+
{
108+
string twitterLink = $"[link=https://twitter.com/{propertyValue}]{propertyValue}[/]";
109+
table.AddRow(propertyName, twitterLink).Border(TableBorder.Square);
110+
}
111+
else if (property.Key == "blog")
112+
{
113+
string blogLink = $"[link={propertyValue}]{propertyValue}[/]";
114+
table.AddRow(propertyName, blogLink).Border(TableBorder.Square);
115+
}
116+
else if (property.Key == "location")
117+
{
118+
string sanitizedLocation = Uri.EscapeDataString(propertyValue);
119+
string locationLink = $"[link=https://www.bing.com/search?q={sanitizedLocation}]{propertyValue}[/]";
120+
table.AddRow(propertyName, locationLink).Border(TableBorder.Square);
121+
}
122+
else if (property.Key == "email")
123+
{
124+
string emailLink = $"[link=mailto:{propertyValue}]{propertyValue}[/]";
125+
table.AddRow(propertyName, emailLink).Border(TableBorder.Square);
126+
}
127+
else
128+
{
129+
table.AddRow(propertyName, propertyValue).Border(TableBorder.Square);
130+
}
131+
}
132+
}
133+
}
134+
135+
table.LeftAligned();
136+
137+
AnsiConsole.Write(table);
138+
}
139+
else
140+
{
141+
AnsiConsole.Markup("[red]无法获取用户信息,请稍后再试。[/]\n");
142+
}
143+
}
144+
145+
private static bool ValidateUsername(string input)
146+
{
147+
if (string.IsNullOrWhiteSpace(input)) return false;
148+
string pattern = @"^[a-zA-Z0-9_-]+$";
149+
return Regex.IsMatch(input, pattern);
150+
}
151+
}
152+
}

ghv/Program.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace ghv
55
{
66
class Program
77
{
8-
public const string Version = "1.0.2";
8+
public const string Version = "develop";
99

1010
static async Task Main(string[] args)
1111
{
@@ -46,6 +46,10 @@ static async Task Main(string[] args)
4646
repo = args.Length > 2 ? args[2] : string.Empty;
4747
await Labels.ExecuteAsync(owner, repo);
4848
break;
49+
case "user":
50+
string username = args.Length > 1 ? args[1] : string.Empty;
51+
await User.ExecuteAsync(username);
52+
break;
4953
// ...更多命令...
5054
default:
5155
AnsiConsole.Markup("[red]无效的命令。[/]\n");

ghv/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"ghv": {
44
"commandName": "Project",
5-
"commandLineArgs": "ctb DuckDuckStudio Fufu_Tools"
5+
"commandLineArgs": "help"
66
}
77
}
88
}

0 commit comments

Comments
 (0)