Add multirealm support (#45)

This commit is contained in:
Brandon Sturgeon 2023-09-20 20:28:46 -07:00 committed by GitHub
parent 70df976f0a
commit 1111203a31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 140 additions and 55 deletions

View File

@ -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

View File

@ -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
@ -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"
@ -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

View File

@ -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
@ -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

View File

@ -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!
@ -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
@ -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

View File

@ -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"

View File

@ -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;

View File

@ -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:

View File

@ -13,9 +13,10 @@ base_url = os.getenv("AVATAR_SERVICE_URL")
@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)

View File

@ -0,0 +1,3 @@
PORT=9103
SENTRY_DSN="https://dc69197dffc94fcf8cc025c5f00ead45@o380324.ingest.sentry.io/5636934"
DISCORD_TOKEN="LQx6IeNs9mtFICwrrB1xqMB2Fgl5-fvf"

View File

@ -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:

View File

@ -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
@ -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")
@ -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
@ -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)
}
@ -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)

View File

@ -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
}