Skip to content

Commit

Permalink
Cleanup code & Nullable annotations (#7)
Browse files Browse the repository at this point in the history
* Reformated code && nullable check

* Accepted some IDE suggestions

* Extract Version Regex to Extension.cs
  • Loading branch information
84634E1A607A authored Jul 21, 2024
1 parent 16721e0 commit 32d64e6
Show file tree
Hide file tree
Showing 74 changed files with 2,418 additions and 1,539 deletions.
85 changes: 34 additions & 51 deletions ThuInfoWeb/Bots/FeedbackNoticeBot.cs
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");
}
}
}
}
244 changes: 119 additions & 125 deletions ThuInfoWeb/Controllers/ApiController.cs
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 });
}
}
}
Loading

0 comments on commit 32d64e6

Please sign in to comment.