Skip to content

Commit

Permalink
Add multirealm support (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonsturgeon authored Sep 21, 2023
1 parent 70df976 commit 1111203
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 55 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# cfc_chat_transit
Paving paths and establishing tunnels

## Convars

### Server
- **`cfc_avatar_service_address`**
- The domain (`avatar_api.mydomain.com`) of the Avatar Service API

- **`cfc_avatar_service_image_address`**
- The domain (`avatars.mydomain.com`) that the Avatar images are actually served from

- **`cfc_relay_host`**
- The domain (`relay.mydomain.com`) of the `discord_relay` service

- `cfc_realm`
- The Realm (`cfc3` / `cfcttt` / `darkrp`) of the server that is running the addon

- **`cfc_chat_transit_should_transmit_remote`**
- Whether or not to send Discord messages to players

- **`cfc_chat_transit_transmit_admin_only`**
- Whether or not to send Discord messages to only Admin+


### Client
- **`cfc_chat_transit_remote_messages`**
- Whether or not Discord messages should appear in Chat
21 changes: 9 additions & 12 deletions moon/cfc_chat_transit/client/receive_remote_message.moon
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import Start, Receive, ReadBool, ReadColor, ReadString, WriteBool, SendToServer from net
import AddToolCategory, AddToolMenuOption from spawnmenu

shouldReceiveRemoteMessages = CreateConVar "cfc_chat_transit_remote_messages", 1, FCVAR_ARCHIVE, "Should receive remote messges in chat", 0, 1

colors =
white: Color 255, 255, 255
blurple: Color 142, 163, 247

Receive "CFC_ChatTransit_RemoteMessageReceive", ->
net.Receive "CFC_ChatTransit_RemoteMessageReceive", ->
return unless shouldReceiveRemoteMessages\GetBool!

author = ReadString!
authorColor = ReadColor!
message = ReadString!
author = net.ReadString!
authorColor = net.ReadColor!
message = net.ReadString!

return unless author
return unless authorColor
Expand All @@ -30,9 +27,9 @@ Receive "CFC_ChatTransit_RemoteMessageReceive", ->
chat.AddText unpack addTextParams

alertPreference = (val) ->
Start "CFC_ChatTransit_RemoteMessagePreference"
WriteBool val
SendToServer!
net.Start "CFC_ChatTransit_RemoteMessagePreference"
net.WriteBool val
net.SendToServer!

initHookName = "CFC_ChatTransit_AlertRemoteMessagePreference"

Expand All @@ -50,8 +47,8 @@ populatePanel = (panel) ->
.OnChange = (_, val) -> alertPreference val

hook.Add "AddToolMenuCategories", "CFC_ChatTransit_MenuCategory", ->
AddToolCategory "Options", "CFC", "CFC"
spawnmenu.AddToolCategory "Options", "CFC", "CFC"

hook.Add "PopulateToolMenu", "CFC_ChatTransit_MenuOption", ->
AddToolMenuOption "Options", "CFC", "should_receive_remote_messages", "Remote Messages", "", "", (panel) ->
spawnmenu.AddToolMenuOption "Options", "CFC", "should_receive_remote_messages", "Remote Messages", "", "", (panel) ->
populatePanel panel
35 changes: 24 additions & 11 deletions moon/cfc_chat_transit/server/avatar_service.moon
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import TableToJSON from util
HTTP = HTTP

avatarServiceAddress = CreateConVar "cfc_avatar_service_address", "", FCVAR_ARCHIVE + FCVAR_PROTECTED
avatarServiceAPIAddress = CreateConVar "cfc_avatar_service_address", "", FCVAR_ARCHIVE + FCVAR_PROTECTED
avatarServiceImageAddress = CreateConVar "cfc_avatar_service_image_address", "", FCVAR_ARCHIVE + FCVAR_PROTECTED

class AvatarService

new: (logger) =>
@logger = logger\scope "AvatarService"
@outlinerUrl = "#{avatarServiceAddress\GetString!}/outline"
@processedIds = {}
@outlinerUrl = "#{avatarServiceAPIAddress\GetString!}/outline"
@processedIDs = {}

getAvatar: (steamID64) =>
url = steamID64 and "https://avatarservice.cfcservers.org/avatars/#{steamID64}.png" or nil
url and= "#{url}?processed=true" if @processedIds[steamID64]
imageAddress = avatarServiceImageAddress\GetString!
realm = ChatTransit.Realm\GetString!
baseURL = "https://#{imageAddress}/avatars/#{realm}"

url = steamID64 and "#{baseURL}/#{steamID64}.png" or nil
url and= "#{url}?processed=true" if @processedIDs[steamID64]

return url

processAvatar: (avatarUrl, outlineColor, steamID64) =>
body = TableToJSON { :avatarUrl, :outlineColor, steamID: steamID64 }
realm = ChatTransit.Realm\GetString!
body = TableToJSON { :avatarUrl, :outlineColor, :realm, steamID: steamID64 }
@logger\debug "Sending data to outliner: ", body

failed = @logger\error
success = (code, body) ->
@logger\debug "Avatar request succeeded with code: #{code} | Body: #{body}"
@processedIds[steamID64] = true
@processedIDs[steamID64] = true

HTTP
:success
Expand All @@ -48,7 +52,16 @@ hook.Add "CFC_SteamLookup_SuccessfulPlayerData", "CFC_ChatTransit_AvatarService"
return unless dataName == "PlayerSummary"
return unless data

success, err = pcall -> ChatTransit.AvatarService\outlineAvatar ply, data
ErrorNoHaltWithStack err, dataName, ply unless success
ProtectedCall -> ChatTransit.AvatarService\outlineAvatar ply, data

return nil

hook.Add "PlayerDisconnected", "CFC_ChatTransit_AvatarServiceReset", (ply) ->
steamID64 = ply\SteamID64!
if not steamID64
ErrorNoHalt "[ChatTransit] Failed to get player's SteamID64 in PlayerDisconnected"
return

AvatarService.processedIDs[steamID64] = nil

return nil
23 changes: 10 additions & 13 deletions moon/cfc_chat_transit/server/remote_messages.moon
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import IsValid from _G
import AddNetworkString from util
import Start, Receive, ReadBool, WriteColor, WriteString, Send from net
import ToColor from string

AddNetworkString "CFC_ChatTransit_RemoteMessagePreference"
AddNetworkString "CFC_ChatTransit_RemoteMessageReceive"
util.AddNetworkString "CFC_ChatTransit_RemoteMessagePreference"
util.AddNetworkString "CFC_ChatTransit_RemoteMessageReceive"

recipients = RecipientFilter!
adminRecipients = RecipientFilter!
Expand All @@ -13,8 +10,8 @@ shouldTransmit = CreateConVar "cfc_chat_transit_should_transmit_remote", 1, FCVA
adminOnly = CreateConVar "cfc_chat_transit_transmit_admin_only", 1, FCVAR_ARCHIVE, "Should only transmit to Admins?", 0, 1

-- TODO: Handle rank changes (to/from Admin)
Receive "CFC_ChatTransit_RemoteMessagePreference", (_, ply) ->
shouldReceive = ReadBool!
net.Receive "CFC_ChatTransit_RemoteMessagePreference", (_, ply) ->
shouldReceive = net.ReadBool!

if shouldReceive
recipients\AddPlayer ply
Expand All @@ -40,13 +37,13 @@ broadcastMessage = (ply, cmd, args, argStr) ->
return unless authorColor
return unless message

authorColor = ToColor authorColor
authorColor = string.ToColor authorColor
sendingTo = adminOnly\GetBool! and adminRecipients or recipients

Start "CFC_ChatTransit_RemoteMessageReceive"
WriteString author
WriteColor authorColor
WriteString message
Send sendingTo
net.Start "CFC_ChatTransit_RemoteMessageReceive"
net.WriteString author
net.WriteColor authorColor
net.WriteString message
net.Send sendingTo

concommand.Add "chat_transit", broadcastMessage
4 changes: 4 additions & 0 deletions web/avatar_service/.env_example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
PORT=9101
API_PORT=9100
AVATARS_DIR="/some/path/to/a/dir/on/host"
AVATAR_SERVICE_URL="https://avatars.mydomain.com"
3 changes: 2 additions & 1 deletion web/avatar_service/avatars.conf
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ http {
listen 80;

location /avatars/ {
add_header 'Cache-Control' 'public, s-maxage 3600, proxy-revalidate';
# add_header 'Cache-Control' 'public, s-maxage 3600, proxy-revalidate';
add_header 'Cache-Control' 'no-store';
include /etc/nginx/mime.types;
root /usr/share/nginx/html;
autoindex on;
Expand Down
2 changes: 1 addition & 1 deletion web/avatar_service/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ services:
context: .
dockerfile: Dockerfile
command: flask run --host 0.0.0.0 --port 8080
container_name: "${REALM}_chat_transit_avatar_service"
container_name: chat_transit_avatar_service
ports:
- "127.0.0.1:$PORT:8080"
environment:
Expand Down
3 changes: 2 additions & 1 deletion web/avatar_service/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
@app.route("/outline", methods=["POST"])
def outline() -> str:
content = request.json
realm = content["realm"]
steam_id = content["steamID"]

avatar_path = f"/avatars/{steam_id}.png"
avatar_path = f"/avatars/{realm}/{steam_id}.png"

if os.path.isfile(avatar_path):
os.remove(avatar_path)
Expand Down
3 changes: 3 additions & 0 deletions web/discord_relay/.env_example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PORT=9103
SENTRY_DSN="https://dc69197dffc94fcf8cc025c5f00ead45@o380324.ingest.sentry.io/5636934"
DISCORD_TOKEN="LQx6IeNs9mtFICwrrB1xqMB2Fgl5-fvf"
2 changes: 1 addition & 1 deletion web/discord_relay/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.8"
services:
web:
build: .
container_name: "${REALM}_chat_transit_discord_relay"
container_name: "chat_transit_discord_relay"
ports:
- "127.0.0.1:$PORT:8080"
env_file:
Expand Down
34 changes: 19 additions & 15 deletions web/discord_relay/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/bwmarrin/discordgo"
"github.com/cfc-servers/cfc_chat_transit/voice"
"github.com/cfc-servers/cfc_chat_transit/webhook"
)

var discord *discordgo.Session
Expand Down Expand Up @@ -42,9 +43,6 @@ type VoiceMessageOperation struct {

var MessageQueue = make(chan []byte, 10000)

var WebhookId string = os.Getenv("WEBHOOK_ID")
var WebhookSecret string = os.Getenv("WEBHOOK_SECRET")

var VoiceWebhookId string = os.Getenv("VOICE_WEBHOOK_ID")
var VoiceWebhookSecret string = os.Getenv("VOICE_WEBHOOK_SECRET")

Expand All @@ -53,16 +51,16 @@ var DiscordToken string = os.Getenv("DISCORD_TOKEN")
const urlRegexString = `https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)`

const (
EMOJI_JOIN = "<:green_cross_cir:654105378933571594>"
EMOJI_LEAVE = "<:circle_red:855605697978957854>"
EMOJI_HALTED = "<:halted:398133588010336259>"
EMOJI_BUILD = "<:build:933512140395012107>"
EMOJI_PVP = "<:bk:812130062379515906>"
EMOJI_PLAY = "<:playbuttonsmaller:1017716044485382154>"
EMOJI_MAP = "🗺️"
EMOJI_CONNECT = "📡"
EMOJI_ULX = "⌨️"
EMOJI_VOICE = "🗣️"
EMOJI_JOIN = "<:green_cross_cir:654105378933571594>"
EMOJI_LEAVE = "<:circle_red:855605697978957854>"
EMOJI_HALTED = "<:halted:398133588010336259>"
EMOJI_BUILD = "<:build:933512140395012107>"
EMOJI_PVP = "<:bk:812130062379515906>"
EMOJI_PLAY = "<:playbuttonsmaller:1017716044485382154>"
EMOJI_MAP = "🗺️"
EMOJI_CONNECT = "📡"
EMOJI_ULX = "⌨️"
EMOJI_VOICE = "🗣️"
EMOJI_ROUND_MODIFIER = "🔵"

COLOR_RED = 0xE7373E
Expand Down Expand Up @@ -101,7 +99,10 @@ func sendMessage(discord *discordgo.Session, message EventStruct) {
AvatarURL: message.Data.Avatar,
}

_, err := discord.WebhookExecute(WebhookId, WebhookSecret, true, params)
realm := message.Realm
webhookInfo := webhook.Get(realm)

_, err := discord.WebhookExecute(webhookInfo.ID, webhookInfo.Secret, true, params)
if err != nil {
log.Println("WebhookExecute errored: ", err)
}
Expand All @@ -122,7 +123,10 @@ func sendEvent(discord *discordgo.Session, event EventStruct, eventText string,
},
}

message, err := discord.WebhookExecute(WebhookId, WebhookSecret, true, params)
realm := event.Realm
webhookInfo := webhook.Get(realm)

message, err := discord.WebhookExecute(webhookInfo.ID, webhookInfo.Secret, true, params)

if err != nil {
log.Println(err)
Expand Down
39 changes: 39 additions & 0 deletions web/discord_relay/webhook/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package webhook

import (
"fmt"
"os"
"sync"
)

type WebhookInfo struct {
ID string
Secret string
}

var cache = make(map[string]*WebhookInfo)
var mu sync.Mutex

func Get(realm string) *WebhookInfo {
mu.Lock()
defer mu.Unlock()

if info, exists := cache[realm]; exists {
return info
}

// Expects cfc3_WEBHOOK_ID / cfc3_WEBHOOK_SECRET
idEnv := fmt.Sprintf("%s_WEBHOOK_ID", realm)
secretEnv := fmt.Sprintf("%s_WEBHOOK_SECRET", realm)

id := os.Getenv(idEnv)
secret := os.Getenv(secretEnv)

info := &WebhookInfo{
ID: id,
Secret: secret,
}

cache[realm] = info
return info
}

0 comments on commit 1111203

Please sign in to comment.