Skip to content

Commit

Permalink
Send Featured Levels to Webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
LimeGradient committed Jul 6, 2024
1 parent b2af449 commit 2de46f1
Show file tree
Hide file tree
Showing 17 changed files with 313 additions and 61 deletions.
Binary file added resources/sheet1/featured/icon-send-btn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions server/central/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ pub struct ServerConfig {

#[serde(default = "default_string")]
pub admin_webhook_url: String,
#[serde(default = "default_string")]
pub featured_webhook_url: String,

// chat limits
#[serde(default = "default_chat_burst_limit")]
Expand Down
1 change: 1 addition & 0 deletions server/central/src/web/routes/game_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub async fn boot(
admin_key: FastString::new(&config.admin_key),
whitelist: config.userlist_mode == UserlistMode::Whitelist,
admin_webhook_url: config.admin_webhook_url.clone(),
featured_webhook_url: config.featured_webhook_url.clone(),
chat_burst_limit: config.chat_burst_limit,
chat_burst_interval: config.chat_burst_interval,
roles: config.roles.clone(),
Expand Down
17 changes: 14 additions & 3 deletions server/game/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,23 @@ impl CentralBridge {
#[inline]
pub async fn send_webhook_message(&self, message: WebhookMessage) -> Result<()> {
let messages = [message];
self.send_webhook_messages(&messages).await
self.send_webhook_messages(&messages, false).await
}

#[inline]
pub async fn send_featured_webhook_message(&self, message: WebhookMessage) -> Result<()> {
let messages = [message];
self.send_webhook_messages(&messages, true).await
}

// not really bridge but it was making web requests which is sorta related i guess
pub async fn send_webhook_messages(&self, messages: &[WebhookMessage]) -> Result<()> {
let url = self.central_conf.lock().admin_webhook_url.clone();
pub async fn send_webhook_messages(&self, messages: &[WebhookMessage], is_featured_webhook: bool) -> Result<()> {
let url: String;
if is_featured_webhook {
url = self.central_conf.lock().featured_webhook_url.clone();
} else {
url = self.central_conf.lock().admin_webhook_url.clone();
}

let mut embeds = Vec::new();

Expand Down
1 change: 1 addition & 0 deletions server/game/src/client/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ impl ClientThread {
AdminDisconnectPacket::PACKET_ID => self.handle_admin_disconnect(&mut data).await,
AdminGetUserStatePacket::PACKET_ID => self.handle_admin_get_user_state(&mut data).await,
AdminUpdateUserPacket::PACKET_ID => self.handle_admin_update_user(&mut data).await,
AdminSendFeaturedLevelPacket::PACKET_ID => self.handle_admin_send_featured_level(&mut data).await,
x => Err(PacketHandlingError::NoHandler(x)),
}
}
Expand Down
30 changes: 23 additions & 7 deletions server/game/src/client/thread/handlers/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl ClientThread {
))
.await
{
warn!("webhook error: {err}");
warn!("webhook error during notice to everyone: {err}");
}
}

Expand Down Expand Up @@ -216,7 +216,7 @@ impl ClientThread {
.send_webhook_message(WebhookMessage::NoticeToPerson(self_name, player_name, notice_msg))
.await
{
warn!("webhook error: {err}");
warn!("webhook error during notice to person: {err}");
}
}

Expand Down Expand Up @@ -282,7 +282,7 @@ impl ClientThread {
.send_webhook_message(WebhookMessage::NoticeToSelection(self_name, threads.len(), notice_msg))
.await
{
warn!("webhook error: {err}");
warn!("webhook error during notice to selection: {err}");
}
}

Expand Down Expand Up @@ -324,7 +324,7 @@ impl ClientThread {
.send_webhook_message(WebhookMessage::KickEveryone(self_name, packet.message.try_to_string()))
.await
{
warn!("webhook error: {err}");
warn!("webhook error during kick everyone: {err}");
}

return Ok(());
Expand All @@ -350,7 +350,7 @@ impl ClientThread {
))
.await
{
warn!("webhook error: {err}");
warn!("webhook error during kick person: {err}");
}
}

Expand Down Expand Up @@ -636,8 +636,8 @@ impl ClientThread {
));
}

if let Err(err) = self.game_server.bridge.send_webhook_messages(&messages).await {
warn!("webhook error: {err}");
if let Err(err) = self.game_server.bridge.send_webhook_messages(&messages, false).await {
warn!("webhook error during roles changed: {err}");
}
}

Expand All @@ -652,4 +652,20 @@ impl ClientThread {
}
}
});

gs_handler!(self, handle_admin_send_featured_level, AdminSendFeaturedLevelPacket, packet, {
if let Err(err) = self.game_server.bridge.send_featured_webhook_message(WebhookMessage::FeaturedLevelSend(
packet.mod_name.clone().to_string(),
packet.level_name.clone().to_string(),
packet.level_id.clone(),
packet.level_author.clone().to_string(),
packet.rate_tier.clone(),
packet.notes.clone(),
))
.await {
warn!("webhook error during send featured: {err}");
}

return Ok(());
});
}
11 changes: 11 additions & 0 deletions server/game/src/data/packets/client/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,14 @@ pub struct AdminGetUserStatePacket {
pub struct AdminUpdateUserPacket {
pub user_entry: UserEntry,
}

#[derive(Packet, Decodable)]
#[packet(id = 19005)]
pub struct AdminSendFeaturedLevelPacket {
pub mod_name: FastString,
pub level_name: FastString,
pub level_id: i32,
pub level_author: FastString,
pub rate_tier: i32,
pub notes: Option<String>
}
62 changes: 62 additions & 0 deletions server/game/src/webhook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum WebhookMessage {
UserViolationMetaChanged(String, String, bool, bool, Option<i64>, Option<String>), // mod username, username, is_banned, is_muted, expiry, reason
UserRolesChanged(String, String, Vec<String>, Vec<String>), // mod username, username, old roles, new roles
UserNameColorChanged(String, String, Option<String>, Option<String>), // mod username, username, old color, new color
FeaturedLevelSend(String, String, i32, String, i32, Option<String>), // mod username, level name, level id, level author, rate tier, notes
}

#[derive(Serialize)]
Expand All @@ -45,6 +46,11 @@ pub struct WebhookField<'a> {
pub inline: Option<bool>,
}

#[derive(Serialize)]
pub struct WebhookThumbnail<'a> {
pub url: &'a str,
}

#[derive(Serialize)]
pub struct WebhookEmbed<'a> {
pub title: String,
Expand All @@ -58,6 +64,8 @@ pub struct WebhookEmbed<'a> {
pub footer: Option<WebhookFooter<'a>>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub fields: Vec<WebhookField<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail: Option<WebhookThumbnail<'a>>,
}

#[derive(Serialize)]
Expand Down Expand Up @@ -87,6 +95,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
value: username.clone(),
inline: Some(true),
}],
thumbnail: None,
}),
WebhookMessage::NoticeToSelection(username, player_count, message) => Some(WebhookEmbed {
title: "Notice".to_owned(),
Expand All @@ -106,6 +115,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
inline: Some(true),
},
],
thumbnail: None,
}),
WebhookMessage::NoticeToPerson(author, target, message) => Some(WebhookEmbed {
title: format!("Notice for {target}"),
Expand All @@ -121,6 +131,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
value: author.clone(),
inline: Some(true),
}],
thumbnail: None,
}),
WebhookMessage::KickEveryone(username, reason) => Some(WebhookEmbed {
title: "Kick everyone".to_owned(),
Expand All @@ -133,6 +144,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
value: username.clone(),
inline: Some(true),
}],
thumbnail: None,
}),
WebhookMessage::KickPerson(mod_name, user_name, target_id, reason) => Some(WebhookEmbed {
title: "Kick user".to_owned(),
Expand All @@ -148,6 +160,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
value: mod_name.clone(),
inline: Some(true),
}],
thumbnail: None,
}),
WebhookMessage::UserBanChanged(bmsc) => Some(WebhookEmbed {
title: if bmsc.new_state {
Expand Down Expand Up @@ -190,6 +203,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
inline: Some(true),
}]
},
thumbnail: None,
}),
WebhookMessage::UserMuteChanged(bmsc) => Some(WebhookEmbed {
title: if bmsc.new_state {
Expand Down Expand Up @@ -232,6 +246,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
inline: Some(true),
}]
},
thumbnail: None,
}),
WebhookMessage::UserViolationMetaChanged(mod_name, user_name, is_banned, _is_muted, expiry, reason) => Some(WebhookEmbed {
title: format!("{} state changed", if *is_banned { "Ban" } else { "Mute" }),
Expand Down Expand Up @@ -259,6 +274,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
inline: Some(false),
},
],
thumbnail: None,
}),
WebhookMessage::UserRolesChanged(mod_name, user_name, old_roles, new_roles) => Some(WebhookEmbed {
title: "Role change".to_owned(),
Expand Down Expand Up @@ -286,6 +302,7 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
inline: Some(true),
},
],
thumbnail: None,
}),
WebhookMessage::UserNameColorChanged(mod_name, user_name, old_color, new_color) => Some(WebhookEmbed {
title: "Name color change".to_owned(),
Expand Down Expand Up @@ -313,7 +330,43 @@ pub fn embed_for_message(message: &WebhookMessage) -> Option<WebhookEmbed> {
inline: Some(true),
},
],
thumbnail: None,
}),
WebhookMessage::FeaturedLevelSend(mod_name, level_name, level_id, level_author, rate_tier, notes) => Some(WebhookEmbed {
title: "New level send".to_owned(),
color: hex_color_to_decimal("#79bd31"),
author: Some(WebhookAuthor {
name: mod_name.clone(),
icon_url: None,
}),
description: None,
footer: None,
fields: vec![
WebhookField {
name: "Level ID",
value: level_id.clone().to_string(),
inline: Some(true),
},
WebhookField {
name: "Level Author",
value: level_author.clone(),
inline: Some(true),
},
WebhookField {
name: "Level Name",
value: level_name.clone(),
inline: Some(true),
},
WebhookField {
name: "Notes",
value: notes.clone().unwrap_or_else(|| "None".to_owned()),
inline: Some(true),
},
],
thumbnail: Some(WebhookThumbnail {
url: rate_tier_to_image(rate_tier),
}),
})
}
}

Expand All @@ -322,3 +375,12 @@ pub fn hex_color_to_decimal(color: &str) -> Option<u32> {

u32::from_str_radix(color, 16).ok()
}

pub fn rate_tier_to_image(tier: &i32) -> &str {
match tier {
0 => return "https://cdn.discordapp.com/attachments/1205711281114587256/1257823701261357106/icon-featured.png?ex=66891adb&is=6687c95b&hm=da86bee4e229695f21cabd491a7a6ef1b16be1a311b225623cda88b6d9eb6f34&",
1 => return "https://cdn.discordapp.com/attachments/1205711281114587256/1257823701517340725/icon-epic.png?ex=66891adb&is=6687c95b&hm=7e606829be057f1602de005965953e0fbe2de015dde6b225716fc664991e9d4d&",
2 => return "https://cdn.discordapp.com/attachments/1205711281114587256/1257823701752348783/icon-outstanding.png?ex=66891adb&is=6687c95b&hm=daa702841cf5a4fdaecb8104d4ae3c3904cdc4440a926d603d60ad81f29833e9&",
_ => return "https://static.wikia.nocookie.net/geometry-dash-creations/images/1/13/Easy_Icon.webp/revision/latest?cb=20220606175015",
}
}
1 change: 1 addition & 0 deletions server/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ By default, the file is created with the name `central-conf.json` in the current
| `userlist_mode` | `"none"` | Can be `blacklist`, `whitelist`, `none` (same as `blacklist`). When set to `whitelist`, players will need to be first whitelisted before being able to join |
| `tps` | `30` | Dictates how many packets per second clients can (and will) send when in a level. Higher = smoother experience but more processing power and bandwidth |
| `admin_webhook_url` | `(empty)` | When enabled, admin actions (banning, muting, etc.) will send a message to the given discord webhook URL |
| `featured_webhook_url` | `(empty)` | When enabled, sending a level to be featured will send a message to the given discord webhook URL |
| `chat_burst_limit` | `0` | Controls the amount of text chat messages users can send in a specific period of time, before getting rate limited. 0 to disable |
| `chat_burst_interval` | `0` | Controls the period of time for the `chat_burst_limit_setting`. Time is in milliseconds |
| `roles` | `(...)` | Controls the roles available on the server (moderator, admin, etc.), their permissions, name colors, and various other things |
Expand Down
4 changes: 3 additions & 1 deletion server/shared/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct GameServerBootData {
pub admin_key: FastString,
pub whitelist: bool,
pub admin_webhook_url: String,
pub featured_webhook_url: String,
pub chat_burst_limit: u32,
pub chat_burst_interval: u32,
pub roles: Vec<ServerRole>,
Expand All @@ -35,6 +36,7 @@ impl Default for GameServerBootData {
admin_key: generate_alphanum_string(ADMIN_KEY_LENGTH).into(),
whitelist: false,
admin_webhook_url: String::new(),
featured_webhook_url: String::new(),
chat_burst_limit: 0,
chat_burst_interval: 0,
roles: Vec::new(),
Expand Down Expand Up @@ -96,4 +98,4 @@ pub struct ServerRole {
pub edit_featured_levels: bool,
#[serde(default)]
pub admin: bool,
}
}
20 changes: 20 additions & 0 deletions src/data/packets/client/admin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,23 @@ class AdminUpdateUserPacket : public Packet {
GLOBED_SERIALIZABLE_STRUCT(AdminUpdateUserPacket, (
userEntry
));

// 19005 - AdminSendFeaturedLevelPacket
class AdminSendFeaturedLevelPacket : public Packet {
GLOBED_PACKET(19005, AdminSendFeaturedLevelPacket, false, true)

AdminSendFeaturedLevelPacket() {}
AdminSendFeaturedLevelPacket(const std::string modName, const std::string levelName, const uint32_t levelID, const std::string levelAuthor, const uint32_t rateTier, const std::optional<std::string>& notes)
: modName(modName), levelName(levelName), levelID(levelID), levelAuthor(levelAuthor), rateTier(rateTier), notes(notes) {}

std::string modName;
std::string levelName;
uint32_t levelID;
std::string levelAuthor;
uint32_t rateTier;
std::optional<std::string> notes;
};

GLOBED_SERIALIZABLE_STRUCT(AdminSendFeaturedLevelPacket, (
modName, levelName, levelID, levelAuthor, rateTier, notes
));
26 changes: 26 additions & 0 deletions src/hooks/level_info_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <ui/menu/level_list/level_list_layer.hpp>
#include <ui/menu/featured/featured_list_layer.hpp>
#include <managers/daily_manager.hpp>
#include <managers/admin.hpp>
#include <ui/menu/featured/edit_featured_level_popup.hpp>
#include <net/manager.hpp>
#include <util/gd.hpp>

Expand All @@ -17,6 +19,30 @@ static int& storedRateTier() {
bool HookedLevelInfoLayer::init(GJGameLevel* level, bool challenge) {
if (!LevelInfoLayer::init(level, challenge)) return false;

auto* leftMenu = typeinfo_cast<CCMenu*>(getChildByIDRecursive("left-side-menu"));
// probably need some way to find this without nodeids
if (leftMenu) {
if (AdminManager::get().authorized()) {
auto& role = AdminManager::get().getRole();
if (role.editFeaturedLevels) {
bool plat = this->m_level->isPlatformer();
CCMenuItemSpriteExtra* btn = Build<CCSprite>::createSpriteName("icon-send-btn.png"_spr)
.intoMenuItem([this, plat] {
if (plat)
EditFeaturedLevelPopup::create(this->m_level)->show();
else
FLAlertLayer::create("Error", "Only <cj>Platformer levels</c> are eligible to be <cg>Globed Featured!</c>", "Ok")->show();
})
.id("edit-btn")
.parent(leftMenu);

if (!plat)
btn->setColor({100, 100, 100});
}
leftMenu->updateLayout();
}
}

// i hate myself
if (level->m_levelIndex == 0) {
for (auto child : CCArrayExt<CCNode*>(CCScene::get()->getChildren())) {
Expand Down
Loading

0 comments on commit 2de46f1

Please sign in to comment.