|
| 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 | +} |
0 commit comments