-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cleanup code & Nullable annotations (#7)
* Reformated code && nullable check * Accepted some IDE suggestions * Extract Version Regex to Extension.cs
- Loading branch information
1 parent
16721e0
commit 32d64e6
Showing
74 changed files
with
2,418 additions
and
1,539 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,50 @@ | ||
using System.Buffers.Text; | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography; | ||
using System.Text; | ||
using System.Text.Json; | ||
|
||
namespace ThuInfoWeb.Bots | ||
namespace ThuInfoWeb.Bots; | ||
|
||
public class FeedbackNoticeBot(IConfiguration configuration) | ||
{ | ||
public class FeedbackNoticeBot | ||
{ | ||
private readonly string _url; | ||
private readonly string _secret; | ||
private readonly HttpClient _httpClient; | ||
private readonly bool _internalNetworkMode; | ||
private readonly HttpClient _httpClient = new(); | ||
private readonly bool _internalNetworkMode = bool.Parse(configuration["InternalNetworkMode"] ?? "false"); | ||
private readonly string _secret = configuration["FeishuBots:FeedbackNoticeBot:Secret"] ?? ""; | ||
private readonly string _url = configuration["FeishuBots:FeedbackNoticeBot:Url"] ?? ""; | ||
|
||
public FeedbackNoticeBot(IConfiguration configuration) | ||
{ | ||
this._url = configuration["FeishuBots:FeedbackNoticeBot:Url"]; | ||
this._secret = configuration["FeishuBots:FeedbackNoticeBot:Secret"]; | ||
this._internalNetworkMode = bool.Parse(configuration["InternalNetworkMode"]); | ||
this._httpClient = new HttpClient(); | ||
} | ||
private string GetSign(long timestamp) | ||
{ | ||
var str = $"{timestamp}\n{_secret}"; | ||
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(str)); | ||
var code = hmac.ComputeHash([]); | ||
var sign = Convert.ToBase64String(code); | ||
return sign; | ||
} | ||
|
||
private string GetSign(long timestamp) | ||
public async Task PushNoticeAsync(string content) | ||
{ | ||
if (_internalNetworkMode) | ||
{ | ||
var str = $"{timestamp}\n{_secret}"; | ||
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(str)); | ||
var code = hmac.ComputeHash(new byte[0]); | ||
var sign = Convert.ToBase64String(code); | ||
return sign; | ||
var resp = await _httpClient.PostAsync("https://stu.cs.tsinghua.edu.cn/thuinfo/botnotice", | ||
JsonContent.Create(new { Content = content, Secret = _secret })); | ||
resp.EnsureSuccessStatusCode(); | ||
} | ||
|
||
public async Task PushNoticeAsync(string content) | ||
else | ||
{ | ||
if (_internalNetworkMode) | ||
{ | ||
var resp = await _httpClient.PostAsync("https://stu.cs.tsinghua.edu.cn/thuinfo/botnotice", JsonContent.Create(new | ||
{ | ||
Content = content, | ||
Secret = _secret | ||
})); | ||
resp.EnsureSuccessStatusCode(); | ||
} | ||
else | ||
{ | ||
var ts = DateTimeOffset.Now.ToUnixTimeSeconds(); | ||
var resp = await _httpClient.PostAsync(_url, JsonContent.Create(new | ||
var ts = DateTimeOffset.Now.ToUnixTimeSeconds(); | ||
var resp = await _httpClient.PostAsync(_url, | ||
JsonContent.Create(new | ||
{ | ||
timestamp = ts.ToString(), | ||
sign = GetSign(ts), | ||
msg_type = "text", | ||
content = new | ||
{ | ||
text = content | ||
} | ||
content = new { text = content } | ||
})); | ||
var json = await resp.Content.ReadAsStringAsync(); | ||
var parsed = JsonDocument.Parse(json); | ||
if (!parsed.RootElement.TryGetProperty("StatusCode", out var code)) | ||
throw new Exception("Send error"); | ||
else if (code.GetInt32() != 0) | ||
throw new Exception("Send error"); | ||
else | ||
return; | ||
} | ||
var json = await resp.Content.ReadAsStringAsync(); | ||
var parsed = JsonDocument.Parse(json); | ||
if (!parsed.RootElement.TryGetProperty("StatusCode", out var code)) | ||
throw new Exception("Send error"); | ||
if (code.GetInt32() != 0) | ||
throw new Exception("Send error"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,155 +1,149 @@ | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc; | ||
using ThuInfoWeb.Bots; | ||
using ThuInfoWeb.DBModels; | ||
using ThuInfoWeb.Dtos; | ||
|
||
namespace ThuInfoWeb.Controllers | ||
namespace ThuInfoWeb.Controllers; | ||
|
||
/// <summary> | ||
/// The controller for RESTApi | ||
/// </summary> | ||
[Route("[controller]")] | ||
[ApiController] | ||
public class ApiController(Data data, VersionManager versionManager, FeedbackNoticeBot feedbackNoticeBot) | ||
: ControllerBase | ||
{ | ||
/// <summary> | ||
/// The controller for RESTApi | ||
/// Get announce, get the latest announce simply by no query string(just get /api/announce). If needed, you should only | ||
/// enter id or page at one time. | ||
/// </summary> | ||
[Route("[controller]")] | ||
[ApiController] | ||
public class ApiController : ControllerBase | ||
/// <param name="id">if entered, this will return single value</param> | ||
/// <param name="page">if entered, this will return up to 5 values in an array.</param> | ||
/// <returns>In json format.</returns> | ||
[Route("Announce")] | ||
public async Task<IActionResult> Announce([FromQuery] int? id, [FromQuery] int? page) | ||
{ | ||
private readonly Data _data; | ||
private readonly VersionManager _versionManager; | ||
private readonly FeedbackNoticeBot _feedbackNoticeBot; | ||
|
||
public ApiController(Data data, VersionManager versionManager, FeedbackNoticeBot feedbackNoticeBot) | ||
if (page <= 0) | ||
return BadRequest("page必须是正整数"); | ||
if (page is not null) | ||
{ | ||
this._data = data; | ||
this._versionManager = versionManager; | ||
this._feedbackNoticeBot = feedbackNoticeBot; | ||
var a = await data.GetActiveAnnouncesAsync((int)page, 5); | ||
return Ok(a); | ||
} | ||
|
||
/// <summary> | ||
/// Get announce, get the latest announce simply by no query string(just get /api/announce). If needed, you should only enter id or page at one time. | ||
/// </summary> | ||
/// <param name="id">if entered, this will return single value</param> | ||
/// <param name="page">if entered, this will return up to 5 values in an array.</param> | ||
/// <returns>In json format.</returns> | ||
[Route("Announce")] | ||
public async Task<IActionResult> Announce([FromQuery] int? id, [FromQuery] int? page) | ||
else | ||
{ | ||
if (page is not null && page <= 0) | ||
return BadRequest("page必须是正整数"); | ||
if (page is not null) | ||
{ | ||
var a = await _data.GetActiveAnnouncesAsync(page ?? 1, 5); | ||
return Ok(a); | ||
} | ||
else | ||
{ | ||
var a = await _data.GetActiveAnnounceAsync(id); | ||
return Ok(a); | ||
} | ||
var a = await data.GetActiveAnnounceAsync(id); | ||
return Ok(a); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Create a feedback | ||
/// </summary> | ||
/// <param name="dto">a json, has content, appversion, os, nickname(optional)</param> | ||
/// <returns></returns> | ||
[Route("Feedback"), HttpPost] | ||
public async Task<IActionResult> Feedback(FeedbackDto dto) | ||
/// <summary> | ||
/// Create a feedback | ||
/// </summary> | ||
/// <param name="dto">a json, has content, appversion, os, nickname(optional)</param> | ||
/// <returns></returns> | ||
[Route("Feedback")] | ||
[HttpPost] | ||
public async Task<IActionResult> Feedback(FeedbackDto dto) | ||
{ | ||
var feedback = new Feedback | ||
{ | ||
var feedback = new Feedback() | ||
{ | ||
AppVersion = dto.AppVersion, | ||
Content = dto.Content, | ||
CreatedTime = DateTime.Now, | ||
OS = dto.OS.ToLower(), | ||
Contact = dto.Contact, | ||
PhoneModel = dto.PhoneModel | ||
}; | ||
var result = await _data.CreateFeedbackAsync(feedback); | ||
if (result != 1) return BadRequest(); | ||
else | ||
{ | ||
_ = _feedbackNoticeBot.PushNoticeAsync( | ||
$"收到新反馈\n{dto.Content}\n请前往http://app.cs.tsinghua.edu.cn/Home/Feedback回复"); | ||
return Created("Api/Feedback", null); | ||
} | ||
} | ||
|
||
[Route("RepliedFeedback")] | ||
public async Task<IActionResult> RepliedFeedback() | ||
AppVersion = dto.AppVersion, | ||
Content = dto.Content, | ||
CreatedTime = DateTime.Now, | ||
OS = dto.OS.ToLower(), | ||
Contact = dto.Contact, | ||
PhoneModel = dto.PhoneModel | ||
}; | ||
var result = await data.CreateFeedbackAsync(feedback); | ||
if (result != 1) | ||
{ | ||
return Ok((await _data.GetAllRepliedFeedbacksAsync()) | ||
.Select(x => new | ||
{ | ||
content = x.Content, | ||
reply = x.Reply, | ||
replierName = x.ReplierName ?? "", | ||
repliedTime = x.RepliedTime | ||
}).ToList()); | ||
return BadRequest(); | ||
} | ||
|
||
/// <summary> | ||
/// Get the url content of Wechat group QRCode. | ||
/// </summary> | ||
/// <returns>The url string, NOT in json format.</returns> | ||
[Route("QRCode")] | ||
public async Task<IActionResult> QRCode() | ||
{ | ||
return Ok((await _data.GetMiscAsync()).QrCodeContent); | ||
} | ||
_ = feedbackNoticeBot.PushNoticeAsync( | ||
$"收到新反馈\n{dto.Content}\n请前往http://app.cs.tsinghua.edu.cn/Home/Feedback回复"); | ||
return Created("Api/Feedback", null); | ||
} | ||
|
||
/// <summary> | ||
/// Redirect to the url ok APK. | ||
/// </summary> | ||
/// <returns></returns> | ||
[Route("Apk")] | ||
public async Task<IActionResult> Apk() | ||
{ | ||
// when start for the first time, if the apkurl is null or empty, this will generate an exception, so set an apkurl value as soon as possible. | ||
return Redirect((await _data.GetMiscAsync())?.ApkUrl); | ||
} | ||
[Route("RepliedFeedback")] | ||
public async Task<IActionResult> RepliedFeedback() | ||
{ | ||
return Ok((await data.GetAllRepliedFeedbacksAsync()) | ||
.Select(x => new | ||
{ | ||
content = x.Content, reply = x.Reply, replierName = x.ReplierName, repliedTime = x.RepliedTime | ||
}).ToList()); | ||
} | ||
|
||
[Route("Socket")] | ||
public async Task<IActionResult> Socket([FromQuery] int? sectionId) | ||
/// <summary> | ||
/// Get the url content of Wechat group QRCode. | ||
/// </summary> | ||
/// <returns>The url string, NOT in json format.</returns> | ||
[Route("QRCode")] | ||
public async Task<IActionResult> QRCode() | ||
{ | ||
return Ok((await data.GetMiscAsync())?.QrCodeContent ?? ""); | ||
} | ||
|
||
/// <summary> | ||
/// Redirect to the url ok APK. | ||
/// </summary> | ||
/// <returns></returns> | ||
[Route("Apk")] | ||
public async Task<IActionResult> Apk() | ||
{ | ||
// when start for the first time, if the apkurl is null or empty, this will generate an exception, so set an apkurl value as soon as possible. | ||
return Redirect((await data.GetMiscAsync())?.ApkUrl ?? ""); | ||
} | ||
|
||
[Route("Socket")] | ||
public async Task<IActionResult> Socket([FromQuery] int? sectionId) | ||
{ | ||
if (sectionId is null) | ||
return Ok(new List<SocketDto>()); | ||
|
||
return Ok((await data.GetSocketsAsync((int)sectionId)).Select(x => new SocketDto | ||
{ | ||
if (sectionId is null) | ||
return Ok(new List<SocketDto>()); | ||
CreatedTime = x.CreatedTime, | ||
SeatId = x.SeatId, | ||
SectionId = x.SectionId, | ||
UpdatedTime = x.UpdatedTime, | ||
Status = Parse(x.Status) | ||
}).ToList()); | ||
|
||
static string parse(Socket.SocketStatus status) => status switch | ||
static string Parse(Socket.SocketStatus status) | ||
{ | ||
return status switch | ||
{ | ||
DBModels.Socket.SocketStatus.Available => "available", | ||
DBModels.Socket.SocketStatus.Unavailable => "unavailable", | ||
DBModels.Socket.SocketStatus.Unknown => "unknown" | ||
_ => "unknown" | ||
}; | ||
|
||
return Ok((await _data.GetSocketsAsync(sectionId ?? 0)).Select(x => new SocketDto() | ||
{ | ||
CreatedTime = x.CreatedTime, | ||
SeatId = x.SeatId, | ||
SectionId = x.SectionId, | ||
UpdatedTime = x.UpdatedTime, | ||
Status = parse(x.Status) | ||
}).ToList()); | ||
} | ||
} | ||
|
||
[HttpPost, Route("Socket")] | ||
public async Task<IActionResult> Socket(SocketDto dto) | ||
{ | ||
var result = await _data.UpdateSocketAsync(dto.SeatId ?? 0, dto.IsAvailable ?? false); | ||
if (result != 1) return BadRequest(); | ||
else return Ok(); | ||
} | ||
[HttpPost] | ||
[Route("Socket")] | ||
public async Task<IActionResult> Socket(SocketDto dto) | ||
{ | ||
var result = await data.UpdateSocketAsync(dto.SeatId ?? 0, dto.IsAvailable ?? false); | ||
if (result != 1) | ||
return BadRequest(); | ||
return Ok(); | ||
} | ||
|
||
[Route("Version/{os}")] | ||
public IActionResult Version([FromRoute] string os) | ||
{ | ||
if (os.ToLower() == "android") return Ok(_versionManager.GetCurrentVersion(VersionManager.OS.Android)); | ||
else return Ok(_versionManager.GetCurrentVersion(VersionManager.OS.IOS)); | ||
} | ||
[Route("Version/{os}")] | ||
public IActionResult Version([FromRoute] string os) | ||
{ | ||
return Ok(os.Equals("android", StringComparison.CurrentCultureIgnoreCase) | ||
? versionManager.GetCurrentVersion(VersionManager.OS.Android) | ||
: versionManager.GetCurrentVersion(VersionManager.OS.IOS)); | ||
} | ||
|
||
[Route("CardIVersion")] | ||
public async Task<IActionResult> CardIVersion() | ||
{ | ||
return Ok(new { Version = (await _data.GetMiscAsync()).CardIVersion }); | ||
} | ||
[Route("CardIVersion")] | ||
public async Task<IActionResult> CardIVersion() | ||
{ | ||
return Ok(new { Version = (await data.GetMiscAsync())?.CardIVersion ?? -1 }); | ||
} | ||
} | ||
} |
Oops, something went wrong.