Skip to content

Commit 90f25fc

Browse files
committed
feat: 將 TwitCasting 直播通知改由 WebHook 接收
1 parent ea29eec commit 90f25fc

17 files changed

Lines changed: 1424 additions & 132 deletions
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using Discord.Commands;
2+
using Discord_Stream_Notify_Bot.DataBase;
3+
using Discord_Stream_Notify_Bot.HttpClients;
4+
5+
namespace Discord_Stream_Notify_Bot.Command.TwitCasting
6+
{
7+
public class TwitCasting : TopLevelModule
8+
{
9+
private readonly MainDbService _mainDbService;
10+
private readonly TwitcastingClient _twitcastingClient;
11+
12+
public TwitCasting(MainDbService mainDbService, TwitcastingClient twitcastingClient)
13+
{
14+
_mainDbService = mainDbService;
15+
_twitcastingClient = twitcastingClient;
16+
}
17+
18+
[RequireContext(ContextType.DM)]
19+
[Command("FixTCDb")]
20+
[Alias("ftcdb")]
21+
[RequireOwner]
22+
public async Task FixTCDbAsync()
23+
{
24+
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
25+
26+
using var db = _mainDbService.GetDbContext();
27+
var needFixList = db.TwitcastingSpider.Where((x) => string.IsNullOrEmpty(x.ChannelId)).ToList();
28+
29+
foreach (var spider in needFixList)
30+
{
31+
var userInfo = await _twitcastingClient.GetUserInfoAsync(spider.ScreenId).ConfigureAwait(false);
32+
if (userInfo != null)
33+
{
34+
spider.ChannelId = userInfo.User.Id;
35+
}
36+
else
37+
{
38+
Log.Error($"Failed to fix TwitCasting Spider for ScreenId: {spider.ScreenId}");
39+
}
40+
}
41+
42+
await db.SaveChangesAsync().ConfigureAwait(false);
43+
44+
await Context.Channel.SendConfirmAsync("Fix TwitCasting Spider Database", $"Fixed {needFixList.Count} entries.").ConfigureAwait(false);
45+
}
46+
}
47+
}

Discord Stream Notify Bot/DataBase/Table/NoticeTwitcastingStreamChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ public class NoticeTwitcastingStreamChannel : DbEntity
44
{
55
public ulong GuildId { get; set; }
66
public ulong DiscordChannelId { get; set; }
7-
public string ChannelId { get; set; }
7+
public string ScreenId { get; set; }
88
public string StartStreamMessage { get; set; } = "";
99
}
1010
}

Discord Stream Notify Bot/DataBase/Table/TwitcastingSpider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public class TwitcastingSpider : DbEntity
44
{
55
public ulong GuildId { get; set; }
66
public string ChannelTitle { get; set; }
7+
public string ScreenId { get; set; }
78
public string ChannelId { get; set; }
89
public bool IsWarningUser { get; set; } = false;
910
public bool IsRecord { get; set; } = false;

Discord Stream Notify Bot/HttpClients/TwitCasting/TwitcastingClient.cs

Lines changed: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Discord_Stream_Notify_Bot.HttpClients.TwitCasting;
1+
using Discord_Stream_Notify_Bot.HttpClients.Twitcasting.Model;
22
using System.Text;
33

44
#nullable enable
@@ -11,6 +11,7 @@ public class TwitcastingClient
1111
private readonly HttpClient? _apiHttpClient;
1212

1313
private readonly string? _twitcastingAccessToken;
14+
private static readonly string[] _events = ["livestart"];
1415

1516
public TwitcastingClient(HttpClient httpClient, BotConfig botConfig)
1617
{
@@ -30,10 +31,10 @@ public TwitcastingClient(HttpClient httpClient, BotConfig botConfig)
3031
}
3132

3233
/// <summary>
33-
/// 取得頻道正在直播的資料
34+
/// 取得直播分類資料
3435
/// </summary>
3536
/// <param name="channelId"></param>
36-
/// <returns>如果正在直播,則回傳 (<see langword="true"/>, <see cref="int">影片 Id</see>),否則為 (<see langword="false"/>, <see cref="int">0</see>)</returns>
37+
/// <returns></returns>
3738
public async Task<List<Category>?> GetCategoriesAsync()
3839
{
3940
if (_apiHttpClient == null)
@@ -47,7 +48,7 @@ public TwitcastingClient(HttpClient httpClient, BotConfig botConfig)
4748
}
4849
catch (Exception ex)
4950
{
50-
Log.Error($"TwitCastingClient.GetCategoriesAsync: {ex}");
51+
Log.Error(ex.Demystify(), "TwitCastingClient.GetCategoriesAsync");
5152
return null;
5253
}
5354
}
@@ -70,7 +71,7 @@ public TwitcastingClient(HttpClient httpClient, BotConfig botConfig)
7071
}
7172
catch (Exception ex)
7273
{
73-
Log.Error($"TwitCastingClient.GetNewStreamDataAsync: {ex}");
74+
Log.Error(ex.Demystify(), "TwitCastingClient.GetNewStreamDataAsync");
7475
return null;
7576
}
7677
}
@@ -97,9 +98,146 @@ public TwitcastingClient(HttpClient httpClient, BotConfig botConfig)
9798
}
9899
catch (Exception ex)
99100
{
100-
Log.Error($"TwitCastingClient.GetMovieInfoAsync: {ex}");
101+
Log.Error(ex.Demystify(), "TwitCastingClient.GetMovieInfoAsync");
102+
return null;
103+
}
104+
}
105+
106+
/// <summary>
107+
/// 取得使用者資訊
108+
/// </summary>
109+
/// <param name="userIdOrScreenId">使用者 id 或 screen_id</param>
110+
/// <returns><see cref="GetUserInfoResponse"/></returns>
111+
public async Task<GetUserInfoResponse?> GetUserInfoAsync(string userIdOrScreenId)
112+
{
113+
if (_apiHttpClient == null)
114+
throw new NullReferenceException(nameof(_apiHttpClient));
115+
116+
if (string.IsNullOrEmpty(userIdOrScreenId))
117+
throw new ArgumentNullException(nameof(userIdOrScreenId));
118+
119+
try
120+
{
121+
var json = await _apiHttpClient.GetStringAsync($"users/{userIdOrScreenId}");
122+
var data = JsonConvert.DeserializeObject<GetUserInfoResponse>(json);
123+
return data;
124+
}
125+
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
126+
{
127+
// Not Found
128+
return null;
129+
}
130+
catch (Exception ex)
131+
{
132+
Log.Error(ex.Demystify(), "TwitCastingClient.GetUserInfoAsync");
133+
return null;
134+
}
135+
}
136+
137+
#region WebHook
138+
/// <summary>
139+
/// 取得所有已註冊的 WebHook
140+
/// </summary>
141+
/// <returns>返回 WebHook 列表</returns>
142+
/// <exception cref="NullReferenceException"></exception>
143+
public async Task<List<Webhook>?> GetAllRegistedWebHookAsync()
144+
{
145+
if (_apiHttpClient == null)
146+
throw new NullReferenceException(nameof(_apiHttpClient));
147+
148+
const int pageSize = 50;
149+
int offset = 0;
150+
int allCount = int.MaxValue;
151+
var result = new List<Webhook>();
152+
153+
try
154+
{
155+
while (result.Count < allCount)
156+
{
157+
var url = $"webhooks?limit={pageSize}&offset={offset}";
158+
var jsonResponse = await _apiHttpClient.GetStringAsync(url);
159+
var data = JsonConvert.DeserializeObject<GetAllRegistedWebHookJson>(jsonResponse);
160+
161+
if (data?.Webhooks == null || data.Webhooks.Count == 0)
162+
break;
163+
164+
if (allCount == int.MaxValue)
165+
allCount = data.AllCount;
166+
167+
result.AddRange(data.Webhooks);
168+
offset += pageSize;
169+
}
170+
171+
return result;
172+
}
173+
catch (Exception ex)
174+
{
175+
Log.Error(ex.Demystify(), "TwitCastingClient.GetAllRegistedWebHookAsync");
176+
return null;
177+
}
178+
}
179+
180+
/// <summary>
181+
/// 註冊 WebHook
182+
/// </summary>
183+
/// <param name="channelId"></param>
184+
/// <returns></returns>
185+
/// <exception cref="NullReferenceException"></exception>
186+
public async Task<bool?> RegisterWebHookAsync(string channelId)
187+
{
188+
if (_apiHttpClient == null)
189+
throw new NullReferenceException(nameof(_apiHttpClient));
190+
191+
if (string.IsNullOrEmpty(channelId))
192+
throw new NullReferenceException(nameof(channelId));
193+
194+
try
195+
{
196+
var responseMessage = await _apiHttpClient.PostAsync("webhooks", new StringContent(JsonConvert.SerializeObject(new
197+
{
198+
user_id = channelId,
199+
events = _events
200+
})));
201+
202+
responseMessage.EnsureSuccessStatusCode();
203+
204+
return true;
205+
}
206+
catch (Exception ex)
207+
{
208+
Log.Error(ex.Demystify(), "TwitCastingClient.RegisterWebHookAsync");
209+
return null;
210+
}
211+
}
212+
213+
/// <summary>
214+
/// 取消註冊 WebHook
215+
/// </summary>
216+
/// <param name="channelId"></param>
217+
/// <returns></returns>
218+
/// <exception cref="NullReferenceException"></exception>
219+
public async Task<bool?> RemoveWebHookAsync(string channelId)
220+
{
221+
if (_apiHttpClient == null)
222+
throw new NullReferenceException(nameof(_apiHttpClient));
223+
224+
if (string.IsNullOrEmpty(channelId))
225+
throw new NullReferenceException(nameof(channelId));
226+
227+
try
228+
{
229+
var responseMessage = await _apiHttpClient.DeleteAsync($"webhooks?user_id={channelId}&events[]=livestart");
230+
231+
responseMessage.EnsureSuccessStatusCode();
232+
233+
return true;
234+
}
235+
catch (Exception ex)
236+
{
237+
Log.Error(ex.Demystify(), "TwitCastingClient.RemoveWebHookAsync");
101238
return null;
102239
}
103240
}
241+
#endregion
104242
}
105243
}

Discord Stream Notify Bot/HttpClients/TwitCasting/CategoriesJson.cs renamed to Discord Stream Notify Bot/HttpClients/Twitcasting/Model/CategoriesJson.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Discord_Stream_Notify_Bot.HttpClients.TwitCasting
1+
namespace Discord_Stream_Notify_Bot.HttpClients.Twitcasting.Model
22
{
33
public class Category
44
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Discord_Stream_Notify_Bot.HttpClients.Twitcasting.Model
2+
{
3+
public class GetAllRegistedWebHookJson
4+
{
5+
[JsonProperty("all_count")]
6+
public int AllCount { get; set; }
7+
8+
[JsonProperty("webhooks")]
9+
public List<Webhook> Webhooks { get; set; }
10+
}
11+
12+
public class Webhook
13+
{
14+
[JsonProperty("user_id")]
15+
public string UserId { get; set; }
16+
17+
[JsonProperty("event")]
18+
public string Event { get; set; }
19+
}
20+
}

Discord Stream Notify Bot/HttpClients/TwitCasting/GetMovieInfoResponse.cs renamed to Discord Stream Notify Bot/HttpClients/Twitcasting/Model/GetMovieInfoResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Discord_Stream_Notify_Bot.HttpClients.TwitCasting
1+
namespace Discord_Stream_Notify_Bot.HttpClients.Twitcasting.Model
22
{
33
public class Broadcaster
44
{
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Discord_Stream_Notify_Bot.HttpClients.Twitcasting.Model
4+
{
5+
public class GetUserInfoResponse
6+
{
7+
[JsonProperty("user")]
8+
public Broadcaster User { get; set; }
9+
10+
[JsonProperty("supporter_count")]
11+
public int SupporterCount { get; set; }
12+
13+
[JsonProperty("supporting_count")]
14+
public int SupportingCount { get; set; }
15+
}
16+
}

Discord Stream Notify Bot/HttpClients/TwitCasting/TcBackendStreamData.cs renamed to Discord Stream Notify Bot/HttpClients/Twitcasting/Model/TcBackendStreamData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Discord_Stream_Notify_Bot.HttpClients.TwitCasting
1+
namespace Discord_Stream_Notify_Bot.HttpClients.Twitcasting.Model
22
{
33
public class TcBackendStreamData
44
{

Discord Stream Notify Bot/Interaction/Extensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,17 @@ public static string GetNonApprovedChannelTitleByChannelId(this MainDbContext _,
165165
return channelId;
166166
}
167167

168-
public static string GetTwitCastingChannelTitleByChannelId(this MainDbContext _, string channelId)
168+
public static string GetTwitCastingChannelTitleByScreenId(this MainDbContext _, string screenId)
169169
{
170-
channelId = channelId.Trim();
170+
screenId = screenId.Trim();
171171

172172
using var db = Bot.DbService.GetDbContext();
173173

174174
TwitcastingSpider twitcastingSpider;
175-
if ((twitcastingSpider = db.TwitcastingSpider.AsNoTracking().FirstOrDefault((x) => x.ChannelId == channelId)) != null)
175+
if ((twitcastingSpider = db.TwitcastingSpider.AsNoTracking().FirstOrDefault((x) => x.ScreenId == screenId)) != null)
176176
return twitcastingSpider.ChannelTitle;
177177

178-
return channelId;
178+
return screenId;
179179
}
180180

181181
public static string GetTwitchUserNameByUserId(this MainDbContext _, string userId)

0 commit comments

Comments
 (0)