-
Notifications
You must be signed in to change notification settings - Fork 0
/
Worker.cs
126 lines (103 loc) · 4.52 KB
/
Worker.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
using System.Text.Json;
using Microsoft.Extensions.Options;
namespace StackToDisco;
class Worker : BackgroundService
{
private const string StackApiUrl =
"https://api.stackexchange.com/2.3/questions?fromdate={0}&todate={1}&order=asc&sort=creation&tagged=orleans&site=stackoverflow";
private const string DiscordApiUrl = "https://discord.com/api/v9/channels/{0}/messages";
private readonly HttpClient _soClient = new(new HttpClientHandler { AutomaticDecompression = System.Net.DecompressionMethods.GZip });
private readonly HttpClient _discordClient = new();
private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
private readonly ILogger<Worker> _logger;
private readonly WorkerSettings _settings;
public Worker(ILogger<Worker> logger, IOptions<WorkerSettings> options)
{
_logger = logger;
_settings = options.Value;
_discordClient.DefaultRequestHeaders.Authorization = new("Bot", _settings.DiscordBotToken);
_discordClient.DefaultRequestHeaders.Accept.Add(new("application/json"));
_soClient.DefaultRequestHeaders.Accept.Add(new("application/json"));
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var questions = await GetStackOverflowQuestions();
if (questions is null) continue;
await SendDiscordMessages(questions);
await Task.Delay(_settings.QueryInterval, stoppingToken);
}
}
private async Task SendDiscordMessages(StackQueryResponse questions)
{
List<List<string>> batches = new() { new() }; // absolutely horrifying collection initializer
if (questions.Items.Any())
{
batches[0].Add("Here are Orleans questions I've found:");
int batchLength = batches[0][0].Length;
foreach (var item in questions.Items)
{
// discord message can be 2000 chars long at most
if (batchLength + item.Link.Length + batches[^1].Count > 2000)
{
batches.Add(new ());
batchLength = 0;
}
batches[^1].Add(item.Link);
batchLength += item.Link.Length;
}
}
else
{
batches[0].Add("No questions about Orleans found this time. Bummer!");
}
foreach (var batch in batches)
{
using var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["content"] = string.Join("\n", batch)
});
using var request = new HttpRequestMessage
{
RequestUri = new Uri(string.Format(DiscordApiUrl, _settings.DiscordChannelId)),
Method = HttpMethod.Post,
Content = content
};
var response = await _discordClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Discord api api did not return a success. Status: {status}. Response: {response}",
response.StatusCode,
await response.Content.ReadAsStringAsync());
}
}
}
private async Task<StackQueryResponse?> GetStackOverflowQuestions()
{
var dateTo = ((DateTimeOffset)DateTime.Today).ToUnixTimeSeconds();
var dateFrom = dateTo - _settings.QueryInterval.TotalSeconds;
using var request = new HttpRequestMessage
{
RequestUri = new Uri(string.Format(StackApiUrl, dateFrom, dateTo)),
Method = HttpMethod.Get,
};
var response = await _soClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"StackExchange api api did not return a success. Status: {status}. Response: {response}",
response.StatusCode,
await response.Content.ReadAsStringAsync());
return null;
}
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<StackQueryResponse>(json, _serializerOptions);
}
}
record StackQueryResponse(IEnumerable<StackResponse> Items);
record StackResponse(string Link);