Skip to content

Commit 1012e5e

Browse files
refactor and add discord
1 parent 022b8df commit 1012e5e

File tree

5 files changed

+349
-260
lines changed

5 files changed

+349
-260
lines changed

Builder.cs

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
using System.Net.Http.Headers;
2+
using System.Text;
3+
using System.Text.Json;
4+
using System.Text.RegularExpressions;
5+
using Newtonsoft.Json;
6+
7+
namespace TelegramBuildBot;
8+
9+
public class Builder
10+
{
11+
public Builder(string gitHubToken)
12+
{
13+
GitHubToken = gitHubToken;
14+
}
15+
16+
private static readonly Dictionary<string, string?> RepoToWorkflow = new()
17+
{
18+
{"ethpandaops/armiarma","build-push-armiarma.yml"},
19+
{"dapplion/beacon-metrics-gazer","build-push-beacon-metrics-gazer.yml"},
20+
{"hyperledger/besu","build-push-besu.yml"},
21+
{"ralexstokes/ethereum_consensus_monitor","build-push-consensus-monitor.yml"},
22+
{"sigp/eleel","build-push-eleel.yml"},
23+
{"ledgerwatch/erigon","build-push-erigon.yml"},
24+
{"ethpandaops/ethereum-genesis-generator","build-push-genesis-generator.yml"},
25+
{"ethereumjs/ethereumjs-monorepo","build-push-ethereumjs.yml"},
26+
{"ethereum/nodemonitor","build-push-execution-monitor.yml"},
27+
{"flashbots/builder","build-push-flashbots-builder.yml"},
28+
{"ethereum/go-ethereum","build-push-geth.yml"},
29+
{"ethpandaops/goomy-blob","build-push-goomy-blob.yml"},
30+
{"migalabs/goteth","build-push-goteth.yml"},
31+
{"grandinetech/grandine","build-push-grandine.yml"},
32+
{"sigp/lighthouse","build-push-lighthouse.yml"},
33+
{"chainsafe/lodestar","build-push-lodestar.yml"},
34+
{"ralexstokes/mev-rs","build-push-mev-rs.yml"},
35+
{"nethermindeth/nethermind","build-push-nethermind.yml"},
36+
{"status-im/nimbus-eth1","build-push-nimbus-eth1.yml"},
37+
{"status-im/nimbus-eth2","build-push-nimbus-eth2.yml"},
38+
{"prysmaticlabs/prysm","build-push-prysm.yml"},
39+
{"paradigmxyz/reth","build-push-reth.yml"},
40+
{"consensys/teku","build-push-teku.yml"},
41+
{"mariusvanderwijden/tx-fuzz","build-push-tx-fuzz.yml"},
42+
};
43+
44+
public string GitHubToken { get; set; }
45+
46+
public async Task<string?> ProcessMessage(string messageText)
47+
{
48+
49+
if (messageText.StartsWith("/build") || messageText.StartsWith("/barnabas"))
50+
{
51+
string[] msgSegments = messageText.Split(' ');
52+
if (msgSegments.Length < 2)
53+
{
54+
Console.WriteLine("No repo link supplied");
55+
return "You need to supply a repository link with a branch\\.";
56+
}
57+
58+
string url = msgSegments[1];
59+
60+
var regex = new Regex(@"https:\/\/github\.com\/(.*?)\/tree\/(.*)");
61+
var match = regex.Match(url);
62+
63+
if (match is { Success: true, Groups.Count: 3 })
64+
{
65+
string? repo = match.Groups[1].Value;
66+
string branch = match.Groups[2].Value;
67+
(bool triggerSuccess, List<string> dockerImages, string runUrl) = await TriggerGitHubWorkflow(repo, branch);
68+
if (triggerSuccess)
69+
{
70+
Console.WriteLine($"Build triggered for {repo}/{branch} [Run URL: {runUrl} | DockerImage: {String.Join(':', dockerImages)}]");
71+
return $"Your build was triggered\\. [View run on GitHub]({runUrl})\nDocker Image\\(s\\) once run completed:\n{string.Join('\n', dockerImages.Select(x => $"`{x}`"))}";
72+
}
73+
74+
Console.WriteLine("Unable to trigger build.");
75+
return "Sorry\\. Was unable to trigger your build\\. Likely the repository is not supported\\.";
76+
77+
}
78+
79+
Console.WriteLine("Unable to parse repo");
80+
return "Sorry\\. Was unable to get the repository and branch from the URL\\. Check your URL and try again\\.";
81+
}
82+
83+
return null;
84+
}
85+
private async Task<(bool IsSuccessStatusCode, List<string> dockerImageUrls, string? runUrl)> TriggerGitHubWorkflow(string? repo, string branch)
86+
{
87+
88+
// Register a runner with github
89+
using HttpClient client = new();
90+
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
91+
client.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28");
92+
client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Bearer {GitHubToken}");
93+
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("build-bot", "1"));
94+
95+
var requestData = new {
96+
@ref = "master", // Ref of the build repo
97+
inputs = new {
98+
repository = repo,
99+
@ref = branch
100+
}
101+
};
102+
103+
bool isFork = false;
104+
105+
// Check if given repo is a direct map
106+
if (!RepoToWorkflow.TryGetValue(repo.ToLower(), out string? workflowId))
107+
{
108+
Console.WriteLine($"Repo {repo} not in the workflow map. checking for fork.");
109+
110+
// check for fork
111+
var forkResp = await client.GetAsync($"https://api.github.com/repos/{repo}");
112+
if (forkResp.IsSuccessStatusCode)
113+
{
114+
// Got repo meta
115+
string forkContent = await forkResp.Content.ReadAsStringAsync();
116+
JsonDocument forkJson = System.Text.Json.JsonSerializer.Deserialize<JsonDocument>(forkContent);
117+
118+
// check if fork
119+
isFork = forkJson.RootElement.GetProperty("fork").GetBoolean();
120+
if (!isFork)
121+
{
122+
// Not a fork - irgnore.
123+
return (false, null, String.Empty)!;
124+
}
125+
126+
string? parentRepo = forkJson.RootElement.GetProperty("parent").GetProperty("full_name").GetString();
127+
128+
// Check if we have a workflow for the parent
129+
if (!RepoToWorkflow.TryGetValue(parentRepo.ToLower(), out workflowId))
130+
{
131+
return (false,null, String.Empty)!;
132+
}
133+
134+
Console.WriteLine($"Found valid parent repo at {parentRepo}");
135+
}
136+
else
137+
{
138+
// Unable to grab repo meta - return error
139+
return (false, null, String.Empty)!;
140+
}
141+
142+
}
143+
144+
// Build tag url
145+
146+
// Grab the docker base from the workflow
147+
string dockerBase = Regex.Match(workflowId, @"build-push-(.+)\.yml").Groups[1].Value;
148+
List<string> dockerImageUrls = new();
149+
150+
//string dockerhubPrefix = "{dockerhubPrefix}";
151+
string dockerhubPrefix = "";
152+
if (dockerBase == "prysm")
153+
{
154+
if (isFork)
155+
{
156+
string forkUser = repo.Split('/')[0];
157+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/prysm-beacon-chain:{forkUser}-{branch}");
158+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/prysm-validator:{forkUser}-{branch}");
159+
}
160+
else
161+
{
162+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/prysm-beacon-chain:{branch}");
163+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/prysm-validator:{branch}");
164+
}
165+
}
166+
else if (dockerBase == "nimbus-eth2")
167+
{
168+
if (isFork)
169+
{
170+
string forkUser = repo.Split('/')[0];
171+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/nimbus-eth2:{forkUser}-{branch}");
172+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/nimbus-validator-client:{forkUser}-{branch}");
173+
}
174+
else
175+
{
176+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/nimbus-eth2:{branch}");
177+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/nimbus-validator-client:{branch}");
178+
}
179+
180+
}
181+
else
182+
{
183+
if (isFork)
184+
{
185+
string forkUser = repo.Split('/')[0];
186+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/{dockerBase}:{forkUser}-{branch}");
187+
}
188+
else
189+
{
190+
dockerImageUrls.Add($"{dockerhubPrefix}ethpandaops/{dockerBase}:{branch}");
191+
}
192+
}
193+
194+
// Trigger job
195+
var content = new StringContent(
196+
JsonConvert.SerializeObject(requestData),
197+
Encoding.UTF8,
198+
"application/json");
199+
200+
HttpResponseMessage response = await client.PostAsync(
201+
$"https://api.github.com/repos/ethpandaops/eth-client-docker-image-builder/actions/workflows/{workflowId}/dispatches", content);
202+
203+
// Grab job url from GH
204+
await Task.Delay(1500);
205+
HttpResponseMessage runsResponse = await client.GetAsync(
206+
$"https://api.github.com/repos/ethpandaops/eth-client-docker-image-builder/actions/runs");
207+
208+
string? runUrl = String.Empty;
209+
if(response.IsSuccessStatusCode)
210+
{
211+
string runsContent = await runsResponse.Content.ReadAsStringAsync();
212+
JsonDocument? responseObject = System.Text.Json.JsonSerializer.Deserialize<JsonDocument>(runsContent);
213+
if (responseObject != null)
214+
{
215+
var firstRun = responseObject.RootElement.GetProperty("workflow_runs").EnumerateArray().FirstOrDefault();
216+
runUrl = firstRun.GetProperty("html_url").GetString();
217+
}
218+
}
219+
220+
return (response.IsSuccessStatusCode, dockerImageUrls, runUrl);
221+
}
222+
223+
}

DiscordBot.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Discord;
2+
using Discord.WebSocket;
3+
4+
namespace TelegramBuildBot;
5+
6+
public class DiscordBot
7+
{
8+
private readonly DiscordSocketClient _client;
9+
10+
public DiscordBot()
11+
{
12+
var settings = new DiscordSocketConfig();
13+
settings.GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent;
14+
_client = new DiscordSocketClient(settings);
15+
16+
_client.Log += LogAsync;
17+
_client.MessageReceived += MessageReceivedAsync;
18+
}
19+
20+
private Task LogAsync(LogMessage log)
21+
{
22+
Console.WriteLine($"DISCORD: {log.ToString()}");
23+
return Task.CompletedTask;
24+
}
25+
26+
private async Task MessageReceivedAsync(SocketMessage message)
27+
{
28+
if (message.Author.Id == _client.CurrentUser.Id || message.Author.IsBot)
29+
return;
30+
31+
Console.WriteLine($"DISCORDMSG: {message.Content}");
32+
string? responseMsg = await Program.Builder.ProcessMessage(message.Content);
33+
34+
if (responseMsg != null)
35+
{
36+
await message.Channel.SendMessageAsync(responseMsg);
37+
}
38+
}
39+
40+
public async Task StartBot(string token)
41+
{
42+
await _client.LoginAsync(TokenType.Bot, token);
43+
await _client.StartAsync();
44+
45+
}
46+
}

0 commit comments

Comments
 (0)