Skip to content

Commit c4534b2

Browse files
committed
Implement GH Release announcement (#2).
1 parent b776261 commit c4534b2

File tree

5 files changed

+142
-4
lines changed

5 files changed

+142
-4
lines changed

config.toml.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ id = "1006346052317753414" # ID of the #help forum channel
66
closedTag = "1006926031434813500" # ID of the post tag that corresponds to a closed post
77
openedTag = "1063924583847170098" # ID of the post tag that corresponds to an opened post
88

9+
[releaseChannel]
10+
id = "984553991545446411" # ID of the #release channel
11+
12+
[releaseAlertChannel]
13+
id = "" # ID of the channel where the GitHub webhook is configured
14+
915
[emojis]
1016
coder = "971867583156469840"
1117
vscode = "1078432889995268248"

lib/config.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ final List<ConfigType> configTypes = [
2929
["helpChannel", "id"],
3030
["helpChannel", "closedTag"],
3131
["helpChannel", "openedTag"],
32+
["releaseChannel", "id"],
33+
["releaseAlertChannel", "id"],
3234
["emojis", "coder"],
3335
["emojis", "vscode"],
3436
["emojis", "linux"],

lib/discord/client.dart

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ import "package:codercord/discord/interactions/multiselects/multiselects.dart"
1111

1212
import "package:logging/logging.dart";
1313

14+
import "package:codercord/github/github.dart";
15+
import "package:github/github.dart";
16+
import "package:version/version.dart";
17+
1418
import "package:nyxx/nyxx.dart";
1519
import "package:nyxx_interactions/nyxx_interactions.dart";
1620

1721
final logger = Logger("Codercord");
1822

23+
RegExp linkText = RegExp("(?<=\\[)(.+)(?=\\])");
24+
1925
class Codercord {
2026
final List<PresenceBuilder> presenceList = [
2127
PresenceBuilder.of(activity: ActivityBuilder.game("with Coder OSS")),
@@ -57,6 +63,50 @@ class Codercord {
5763
interactions.syncOnReady();
5864
}
5965

66+
Future<List<Release>> triggerReleaseCheck(RepositorySlug slug) async {
67+
Version? lastSentVersion;
68+
69+
await for (final message in releaseChannel.downloadMessages(limit: 10)) {
70+
if (message.author.id == client.self.id && message.embeds.isNotEmpty) {
71+
IEmbedField versionField = message.embeds[0].fields.firstWhere(
72+
(field) => field.name == "Version",
73+
);
74+
75+
String? messageVersion =
76+
linkText.stringMatch(versionField.content)?.replaceFirst("v", "");
77+
78+
try {
79+
lastSentVersion = Version.parse(messageVersion!);
80+
81+
break;
82+
} catch (_) {}
83+
}
84+
}
85+
86+
List<Release> releasesToSend;
87+
if (lastSentVersion != null) {
88+
releasesToSend = await getNewerReleases(slug, lastSentVersion).then(
89+
(releases) => releases.reversed.toList(),
90+
);
91+
} else {
92+
releasesToSend = [await getNewestRelease(slug)];
93+
}
94+
95+
for (final release in releasesToSend) {
96+
ComponentMessageBuilder releaseMessage = await makeReleaseMessage(
97+
slug,
98+
release,
99+
);
100+
101+
await releaseChannel.sendMessage(releaseMessage);
102+
logger.info(
103+
"Sent release announcement for ${slug.fullName} ${release.tagName!}",
104+
);
105+
}
106+
107+
return releasesToSend;
108+
}
109+
60110
void login() async {
61111
logger.info("Codercord is loading..");
62112

@@ -76,15 +126,18 @@ class Codercord {
76126
exit(1);
77127
}
78128

79-
logger.info("Registering commands..");
80-
await registerInteractionHandlers();
81-
82129
logger.info("Codercord is ready !");
83130

84131
logger.info(
85132
"Invite link: https://discord.com/oauth2/authorize?client_id=$clientId&scope=bot%20applications.commands&permissions=294205377552",
86133
);
87134

135+
logger.info("Registering commands..");
136+
await registerInteractionHandlers();
137+
138+
logger.info("Checking for new releases..");
139+
await triggerReleaseCheck(coderRepo);
140+
88141
client.eventsWs.onThreadCreated.listen((event) async {
89142
if (event.newlyCreated && await event.thread.isHelpPost) {
90143
event.thread.setPostTags([openedTagID]);
@@ -112,6 +165,12 @@ class Codercord {
112165
await event.message.delete(
113166
auditReason: "Automatic deletion of channel pin announcements.",
114167
);
168+
} else if (event.message.channel.id == releaseAlertChannel.id &&
169+
event.message.author.isInteractionWebhook) {
170+
logger.info(
171+
"A new message was sent in the release alert channel, checking for new releases..",
172+
);
173+
await triggerReleaseCheck(coderRepo);
115174
}
116175
});
117176

lib/github/github.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import "package:version/version.dart";
2+
import "package:github/github.dart";
3+
4+
import "package:nyxx_interactions/nyxx_interactions.dart";
5+
import "package:nyxx/nyxx.dart";
6+
7+
final GitHub github = GitHub();
8+
9+
Future<Release> getNewestRelease(RepositorySlug slug) {
10+
return github.repositories.listReleases(slug).first;
11+
}
12+
13+
Future<List<Release>> getNewerReleases(
14+
RepositorySlug slug,
15+
Version version,
16+
) async {
17+
Stream<Release> releaseStream =
18+
github.repositories.listReleases(slug).takeWhile(
19+
(release) {
20+
Version releaseVersion = Version.parse(
21+
release.tagName!.replaceFirst("v", ""),
22+
);
23+
24+
return releaseVersion > version;
25+
},
26+
);
27+
28+
return releaseStream.toList();
29+
}
30+
31+
Future<ComponentMessageBuilder> makeReleaseMessage(
32+
RepositorySlug slug,
33+
Release release,
34+
) async {
35+
EmbedBuilder embed = EmbedBuilder()
36+
..title = "New release published ! :tada:"
37+
..addField(
38+
name: "Repository",
39+
content: "[${slug.fullName}](https://github.com/${slug.fullName})",
40+
inline: true,
41+
)
42+
..addField(
43+
name: "Version",
44+
content: "[${release.tagName!}](${release.htmlUrl!})",
45+
inline: true,
46+
);
47+
48+
ComponentMessageBuilder message = ComponentMessageBuilder()
49+
..componentRows = [
50+
ComponentRowBuilder()
51+
..addComponent(
52+
LinkButtonBuilder("Changelog", release.htmlUrl!),
53+
)
54+
]
55+
..embeds = [embed];
56+
57+
return message;
58+
}

lib/values.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import "package:codercord/config.dart";
22

3+
import "package:github/github.dart";
34
import "package:nyxx/nyxx.dart";
45

56
late final IGuild coderServer;
67
late final IGuild emojiServer;
78

8-
late final IGuildChannel helpChannel;
9+
late final IForumChannel helpChannel;
10+
late final ITextChannel releaseChannel;
11+
late final ITextChannel releaseAlertChannel;
912

1013
late final Snowflake closedTagID;
1114
late final Snowflake openedTagID;
@@ -17,6 +20,8 @@ late final IBaseGuildEmoji linuxEmoji;
1720
late final IBaseGuildEmoji windowsEmoji;
1821
late final IBaseGuildEmoji macosEmoji;
1922

23+
final RepositorySlug coderRepo = RepositorySlug("coder", "coder");
24+
2025
Future<void> loadValues(INyxxWebsocket client) async {
2126
coderServer = await client.fetchGuild(Snowflake(config["coderServer"]["id"]),
2227
withCounts: false);
@@ -25,6 +30,14 @@ Future<void> loadValues(INyxxWebsocket client) async {
2530
Snowflake(config["helpChannel"]["id"]),
2631
);
2732

33+
releaseChannel = await client.fetchChannel(
34+
Snowflake(config["releaseChannel"]["id"]),
35+
);
36+
37+
releaseAlertChannel = await client.fetchChannel(
38+
Snowflake(config["releaseAlertChannel"]["id"]),
39+
);
40+
2841
closedTagID = Snowflake(config["helpChannel"]["closedTag"]);
2942
openedTagID = Snowflake(config["helpChannel"]["openedTag"]);
3043

0 commit comments

Comments
 (0)