Rappelz x Discord: Chat & Logs Sync + Anti-Cheat Alerts

08/27/2025 15:01 chihab007#1
Hi everyone,

So recently I was working on my own Anti-Cheat system for GTA :D, and I noticed that both GTA and Rappelz make strong use of Lua scripting.
That inspired me to try applying some of the same concepts in Rappelz, to see if I could achieve similar results.

In GTA there are already methods to hook Anti-Cheat with Discord (for example: sending alerts/logs directly to a channel).
So I decided to build something similar for Rappelz private servers.

🔹 What I have so far:
- Connected Rappelz server events (Chat, Logs, Cheat alerts, etc.) with Discord via Webhook.
- Right now, it’s one-way integration (Server → Discord).
- My plan is to extend it later into a two-way bridge (Discord ↔ Server).

🔹 Why this could be useful:
- Easy monitoring of chat & logs directly in Discord.
- :handsdown: Instant notifications for suspicious activity or cheat attempts.
- Makes server management more convenient without staying in-game 24/7.

I’m still testing, but it looks promising.
I wanted to share the idea here and get feedback from the community. :)


🔹 Showcase:

PHP Code:
#include <windows.h>
#include <winhttp.h>
#include <string>
#include <vector>
#include <memory>
#pragma comment(lib, "winhttp.lib")

static bool ConvertToUtf8(const std::stringsrcUINT codePagestd::stringout
{
    if (
src.empty()) { out.clear(); return true; }

    
// جرّب التحويل إلى UTF-16
    
int wlen MultiByteToWideChar(codePageMB_ERR_INVALID_CHARSsrc.data(), (int)src.size(), nullptr0);
    if (
wlen <= 0) return false;

    
std::wstring wstr(wlenL'\0');
    if (
MultiByteToWideChar(codePageMB_ERR_INVALID_CHARSsrc.data(), (int)src.size(), &wstr[0], wlen) <= 0)
        return 
false;

    
// ثم UTF-16 إلى UTF-8
    
int u8len WideCharToMultiByte(CP_UTF80wstr.c_str(), wlennullptr0nullptrnullptr);
    if (
u8len <= 0) return false;

    
out.assign(u8len'\0');
    if (
WideCharToMultiByte(CP_UTF80wstr.c_str(), wlen, &out[0], u8lennullptrnullptr) <= 0)
        return 
false;

    return 
true;
}


// دالة تحويل ANSI إلى UTF-8
static std::string ToUTF8(const std::strings) {
    
// الأولوية: UTF-8 -> 1256 -> ACP
    
std::string out;
    if (
ConvertToUtf8(sCP_UTF8out)) return out;

#ifndef CP_ARABIC
#define CP_ARABIC 1256
#endif
    
if (ConvertToUtf8(sCP_ARABICout)) return out;

    if (
ConvertToUtf8(sCP_ACPout)) return out;

    
// فشل كل شيء: أعد النص كما هو
    
return s;
}

std::wstring ToWide(const std::stringnarrow) {
    
int wideSize MultiByteToWideChar(CP_UTF80narrow.c_str(), -1nullptr0);
    if (
wideSize == 0) return L"";

    
std::wstring wideStr(wideSize0);
    
MultiByteToWideChar(CP_UTF80narrow.c_str(), -1, &wideStr[0], wideSize);
    return 
wideStr;
}
#include <string>
#include <stdio.h>

static std::string JsonEscape(const std::strings) {
    
std::string out;
    
out.reserve(s.size() + 16); // موجود من C++98 فما فوق، لكن يمكننا الإبقاء عليها لأنها مجرد optimization
    
for (std::string::size_type i 0s.size(); ++i) {
        
unsigned char c = (unsigned char)s[i];
        switch (
c) {
        case 
'\"'out += "\\\""; break;
        case 
'\\'out += "\\\\"; break;
        case 
'\b'out += "\\b"; break;
        case 
'\f'out += "\\f"; break;
        case 
'\n'out += "\\n"; break;
        case 
'\r'out += "\\r"; break;
        case 
'\t'out += "\\t"; break;
        default:
            if (
0x20) {
                
char buf[7];
                
sprintf(buf"\\u%04X"c); // بدل sprintf_s
                
out += buf;
            }
            else {
                
out += (char)c;
            }
        }
    }
    return 
out;
}



// هيكل لنقل البيانات إلى الـ thread
struct DiscordData {
    
std::string sender;
    
std::string text;
    
std::string webhookUrl// إضافة حقل لرابط الويب هوك

    
DiscordData(const charszSender, const charszText, const charszWebhookUrl)
        : 
sender(szSender szSender ""), text(szText szText ""), webhookUrl(szWebhookUrl szWebhookUrl "") {
    }
};

DWORD WINAPI DiscordThreadFunction(LPVOID lpParam) {
    
std::unique_ptr<DiscordDatadata(static_cast<DiscordData*>(lpParam));

    
// إذا لم يكن هناك رابط ويب هوك، لا ترسل أي شيء
    
if (data->webhookUrl.empty()) return 1;

    
HINTERNET hSession WinHttpOpen(L"DiscordWebhook/1.0",
        
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
        
WINHTTP_NO_PROXY_NAME,
        
WINHTTP_NO_PROXY_BYPASS0);
    if (!
hSession) return 1;

    
HINTERNET hConnect WinHttpConnect(hSessionL"discord.com",
        
INTERNET_DEFAULT_HTTPS_PORT0);
    if (!
hConnect) {
        
WinHttpCloseHandle(hSession);
        return 
1;
    }

    
// استخراج المسار من رابط الويب هوك
    
std::wstring webhookUrlW ToWide(data->webhookUrl);
    
std::wstring path L"/api/webhooks" webhookUrlW.substr(webhookUrlW.find(L"/api/webhooks") + 13);

    
HINTERNET hRequest WinHttpOpenRequest(hConnectL"POST",
        
path.c_str(),
        
NULLWINHTTP_NO_REFERER,
        
WINHTTP_DEFAULT_ACCEPT_TYPES,
        
WINHTTP_FLAG_SECURE);
    if (!
hRequest) {
        
WinHttpCloseHandle(hConnect);
        
WinHttpCloseHandle(hSession);
        return 
1;
    }

    
// تحويل النصوص إلى UTF-8 بشكل موثوق
    
std::string utf8Sender ToUTF8(data->sender);
    
std::string utf8Text ToUTF8(data->text);

    
// إنشاء المحتوى ثم تهريبه لJSON
    
std::string content;
    if (!
utf8Sender.empty()) {
        
content += utf8Sender;
        
content += ": ";
    }
    
content += utf8Text;

    
std::string jsonPayload "{\"content\":\"";
    
jsonPayload += JsonEscape(content);
    
jsonPayload += "\"}";

    
// تعيين headers
    
LPCWSTR headers L"Content-Type: application/json; charset=utf-8";

    
// إرسال الطلب
    
BOOL bResults WinHttpSendRequest(hRequestheaders, -1,
        (
LPVOID)jsonPayload.data(),
        (
DWORD)jsonPayload.size(),
        (
DWORD)jsonPayload.size(), 0);

    if (
bResults) {
        
WinHttpReceiveResponse(hRequestNULL);
    }

    
WinHttpCloseHandle(hRequest);
    
WinHttpCloseHandle(hConnect);
    
WinHttpCloseHandle(hSession);

    return 
0;
}

void SendToDiscordWebhook(const charszSender, const charszText, const charszWebhookUrl) {
    
// إنشاء نسخة من البيانات لإرسالها إلى الـ thread
    
DiscordDatadata = new DiscordData(szSenderszTextszWebhookUrl);

    
// إنشاء thread منفصل
    
CreateThread(NULL0DiscordThreadFunctiondata0NULL);

08/27/2025 15:38 khshe#2
At least someone is working on this project and not asking for money. Thank you so much, my friend.
08/28/2025 22:20 Masumichan#3
You realize this is against the TOS, and giving someone access to your webhook is probably not the best idea? Also, Really like the song could you link me?
08/31/2025 12:34 chihab007#4
Quote:
Originally Posted by Masumichan View Post
You realize this is against the TOS, and giving someone access to your webhook is probably not the best idea? Also, Really like the song could you link me?
the name song : Medieval Celtic Music and Fantasy Celtic Music - 10 Hour No ads
------------
You're right, I completely forgot about that. Any modification to a game's source code could put me at risk of legal action. :D i edit so its only the deiscord code thank you a lot
08/31/2025 16:45 Masumichan#5
Quote:
Originally Posted by chihab007 View Post
the name song : Medieval Celtic Music and Fantasy Celtic Music - 10 Hour No ads
------------
You're right, I completely forgot about that. Any modification to a game's source code could put me at risk of legal action. :D i edit so its only the deiscord code thank you a lot
I'm not referring to changing the games source code, But you're violating the discord TOS by doing so, and frankly it would also not be so hard to extract the webhook from the client meaning I could abuse it and it would be disabled.

I checked your new code, on the client side it is almost correct now, But you realise that opening up a new thread to do such things is very expensive, you have a system managed thread pool you're looking for the QueueUserWorkItem function in the windows header.