-
Notifications
You must be signed in to change notification settings - Fork 0
/
WorkerFactory.cs
125 lines (110 loc) · 4.88 KB
/
WorkerFactory.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
using LibGit2Sharp;
using RDHT_Backend.Enums;
using RDHT_Backend.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace RDHT_Backend
{
internal class WorkerFactory
{
private static uint _SubCount = 0;
private Queue<string> _Channels;
private Repository _Repository;
private List<string> _Changed;
public WorkerFactory(Queue<string> channels, Repository repository, List<string> changed)
{
_Channels = channels;
_Repository = repository;
_Changed = changed;
}
private static string GetClientSettingsSub()
{
switch (Config.Instance.ClientSettingsUrl)
{
case ClientSettingsUrl.Roblox:
return "clientsettings";
case ClientSettingsUrl.CDN:
return "clientsettingscdn";
case ClientSettingsUrl.Both:
uint i = Interlocked.Increment(ref _SubCount);
return i % 2 == 0 ? "clientsettings" : "clientsettingscdn";
default:
throw new Exception($"Unknown url type: {Config.Instance.ClientSettingsUrl}");
}
}
private async Task<IEnumerable<string>> GetOutput(string channel)
{
List<string> output = new List<string>();
foreach (string binaryType in Globals.BinaryTypes)
{
for (int i = 1; i <= Config.Instance.MaxChannelGetRetries; i++)
{
string url = $"https://{GetClientSettingsSub()}.roblox.com/v2/client-version/{binaryType}/channel/{channel}";
//Console.WriteLine(url);
var req = await Globals.Client.GetAsync(url);
string response = await req.Content.ReadAsStringAsync();
// sometimes clientsettings dies and responds with '{"errors":[{"code":0,"message":"InternalServerError"}]}'
// retry if InternalServerError is the status code and "InternalServerError" is in response, since i dont want to parse json
if (req.StatusCode == HttpStatusCode.InternalServerError && response.Contains("InternalServerError"))
{
Console.WriteLine($"[{channel}] {binaryType} Death, retry {i} ({req.StatusCode}) [{response}] [{url}]");
continue;
}
// sometimes clientsettings dies [2]
// this time, we cant do anything about it
else if (response.Contains("Adaptive Concurrency Limits Enforced"))
{
Console.WriteLine("CLIENTSETTINGS IS DEAD!!!");
Environment.Exit(1337);
throw new Exception("CLIENTSETTINGS IS DEAD!!!");
}
else if (response.Contains("Too many requests"))
{
Console.WriteLine($"[{channel}] {binaryType} Ratelimited! Waiting for {Config.Instance.RatelimitWaitTime}s... ({i}) [{url}]");
await Task.Delay(Config.Instance.RatelimitWaitTime * 1000);
continue;
}
else if (!req.IsSuccessStatusCode)
{
Console.WriteLine($"[{channel}] {binaryType} Failure ({req.StatusCode}) [{response}] [{url}]");
break;
}
var json = JsonSerializer.Deserialize<ClientVersion>(response);
output.Add($"{binaryType}: {json?.VersionGuid} [{json?.Version}]");
Console.WriteLine($"[{channel}] {binaryType} Success");
break;
}
}
return output;
}
private async Task Handle(string channel)
{
IEnumerable<string> output = await GetOutput(channel);
var channelFile = channel + ".txt";
var channelPath = Path.Combine(Globals.ClonePath, channelFile);
// check for any changes
if (!File.Exists(channelPath) || !Enumerable.SequenceEqual(await File.ReadAllLinesAsync(channelPath), output))
{
Console.WriteLine($"[{channel}] Changes detected");
_Changed.Add(channel);
await File.WriteAllTextAsync(channelPath, string.Join("\n", output));
}
_Repository.Index.Add(channelFile);
}
public async Task Create()
{
while (_Channels.TryDequeue(out string? channel))
{
if (channel == null)
continue;
await Handle(channel);
}
}
}
}