mirror of
https://github.com/CFC-Servers/cfc_chat_transit.git
synced 2025-03-04 03:03:05 -05:00
Add multirealm support (#45)
This commit is contained in:
parent
70df976f0a
commit
1111203a31
26
README.md
26
README.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
4
web/avatar_service/.env_example
Normal file
4
web/avatar_service/.env_example
Normal 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"
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
3
web/discord_relay/.env_example
Normal file
3
web/discord_relay/.env_example
Normal file
@ -0,0 +1,3 @@
|
||||
PORT=9103
|
||||
SENTRY_DSN="https://dc69197dffc94fcf8cc025c5f00ead45@o380324.ingest.sentry.io/5636934"
|
||||
DISCORD_TOKEN="LQx6IeNs9mtFICwrrB1xqMB2Fgl5-fvf"
|
@ -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:
|
||||
|
@ -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)
|
||||
|
39
web/discord_relay/webhook/webhook.go
Normal file
39
web/discord_relay/webhook/webhook.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user