summaryrefslogtreecommitdiffstats
path: root/syncplay-bridge/syncplay-bridge
blob: 20d8c5fbd4cc81fcc99586b8a04bf5ca089583a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/env bash
# dependencies: bash 4+, jq

# original: https://gist.github.com/ncfavier/55c20a77a3fd72e00407f8889fc6e852
# forked for LibertaCasa by Georg Pfuetzenreuter <georg@lysergic.dev>
# notable changes: use of IRCv3 draft/relaymsg and NickServ authentication using PASS

set -Cu

#. ./config.sh
#: "${verbose:=0}"
#: "${syncplay_host:=syncplay.pl}"
#: "${syncplay_port:=8999}"
#: "${syncplay_nick:=syncplaybridge}"
#: "${syncplay_room:?"can't be empty"}"
#: "${irc_host:=chat.freenode.net}"
#: "${irc_port:=6667}"
#: "${irc_nick:=syncplaybridge}"
#: "${irc_channel:?"can't be empty"}"

verbose=0
syncplay_host='::1'
syncplay_port=8998
syncplay_password=NOT_IMPLEMENTED
syncplay_nick=irc
syncplay_room=%%SP_ROOM%%
irc_host='::1'
irc_port=6667
irc_nick=syncplay
irc_nickserv_nick=syncplay
irc_nickserv_pass='%%IRC_NS_PASS%%'
irc_channel='%%IRC_CHANNEL%%' #including channel prefix such as # or !
#irc_channel='#dev'

irc_send() {
    local args=("$@")
    #echo "DEBUG: ARGS ARE - $args"
    args[0]=${args[0]^^}
    #echo "DEBUG: ARGS ARE - $args"
    if (( ${#args[@]} > 1 )); then
        args[${#args[@]}-1]=:${args[${#args[@]}-1]}
    fi
    printf '%s\r\n' "${args[*]}" >&"$irc_fd"
    (( verbose )) && printf 'IRC %s==>%s %s\n' "$(tput setaf 1)" "$(tput sgr0)" "${args[*]}" >&2
}

irc_getnick() {
        local arg=$1
        echo "$arg" | grep -Po "(?<=\<).*(?=\>)"
}

irc_zwspnick() {
        local arg=$1
        local nick1="${arg:0:${#arg}/2}"
        local nick2="${arg:${#arg}/2}"
        printf "$nick1\u200b$nick2"
}

irc_say() {
    #echo "DEBUG: irc_say $irc_channel - $*"
    local arg="$*"
    #echo "DEBUG: irc_say ARG: $arg"
    local msg="$(echo $arg | cut -d '>' -f 2 | sed -e 's/^[[:space:]]*//')"
    local nick="$(irc_getnick $arg)"
    if [ -n "$nick" ]
    then
            local zwspnick="$(irc_zwspnick $nick)"
            #irc_send relaymsg "$irc_channel" "$zwspnick/sp" "$msg"
            irc_send relaymsg "$irc_channel" "$nick/sp" "$msg"
    fi
    #echo "DEBUG: irc_say NICK $zwspnick"
    #echo "DEBUG: irc_say MSG $msg"
    if [ -z "$nick" ]
    then
            irc_send privmsg "$irc_channel" "$msg"
    fi
}

irc_ctcp() {
    local args=("$@")
    args[0]=${args[0]^^}
    irc_send notice "$sender_nick" $'\1'"${args[*]}"$'\1'
}

syncplay_send() {
    local msg=$(jq -nc "$@")
    printf '%s\r\n' "$msg" >&"$syncplay_fd"
    (( verbose )) && printf 'syncplay %s==>%s %s\n' "$(tput setaf 1)" "$(tput sgr0)" "$msg" >&2
}

syncplay_say() {
    syncplay_send --arg message "$*" '{Chat: $message}'
}

on_exit() {
    irc_send quit
    kill $(jobs -p)
}

exec {irc_fd}<> /dev/tcp/"$irc_host"/"$irc_port" || exit
exec {syncplay_fd}<> /dev/tcp/"$syncplay_host"/"$syncplay_port" || exit

trap on_exit exit

irc_send pass "$irc_nickserv_nick:$irc_nickserv_pass"
irc_send nick "$irc_nick"
irc_send user "$irc_nick" 0 '*' 'Syncplay<->IRC'

# patched version with server password attempt
#syncplay_send --arg username "$syncplay_nick" --arg room "$syncplay_room" --arg password "$syncplay_password" \
#    '{Hello: {$username, password: $password, room: {name: $room}, realversion: "1.6.5"}}'
syncplay_send --arg username "$syncplay_nick" --arg room "$syncplay_room" \
    '{Hello: {$username, room: {name: $room}, realversion: "1.6.5"}}'

while read -ru "$irc_fd" line; do
    line=${line%$'\r'}
    (( verbose )) && printf 'IRC %s<==%s %s\n' "$(tput setaf 2)" "$(tput sgr0)" "$line" >&2
    sender= sender_nick=
    args=()
    if [[ $line == :* ]]; then
        sender=${line%% *}
        sender=${sender#:}
        sender_nick=${sender%%!*}
        line=${line#* }
    fi
    while [[ $line == *' '* && $line != :* ]]; do
        args+=("${line%% *}")
        line=${line#* }
    done
    args+=("${line#:}")
    cmd=${args[0]}
    set -- "${args[@]:1}"
    case ${cmd,,} in
        001) irc_send join "$irc_channel";;
        ping) irc_send pong "$1";;
        privmsg)
            target=$1
            message=$2
            #echo "DEBUG: $target"
            #echo "DEBUG: $message"
            if [[ $target == "$irc_nick" ]] && [[ $message =~ ^$'\1'(.*)$'\1'$ ]]; then
                #echo "DEBUG: privmsg IF 1"
                message=${BASH_REMATCH[1]}
                #echo "DEBUG: BASH REMATCH: $message"
                read -r ctcp_cmd _ <<< "$message"
                case ${ctcp_cmd,,} in
                    version) irc_ctcp version "syncplaybridge";;
                esac
            elif [[ "$target" == "$irc_channel" ]]; then
                #echo "DEBUG: sender_nick IS $sender_nick"
                if [[ ! "$sender_nick" == */sp ]]; then
                        #echo "DEBUG: privmsg IF 2"
                        if [[ $message =~ ^$'\1ACTION '(.*)$'\1'$ ]]; then
                            #echo "DEBUG: privmsg IF 2 $message MATCHES"
                            syncplay_say "* $sender_nick ${BASH_REMATCH[1]}"
                        else
                            #echo "DEBUG: privmsg IF 2 DOES NOT MATCH"
                            syncplay_say "<$sender_nick> $2"
                        fi
                fi
            fi
    esac
done &

while read -ru "$syncplay_fd" line; do
    line=${line%$'\r'}
    (( verbose )) && printf 'syncplay %s<==%s %s\n' "$(tput setaf 2)" "$(tput sgr0)" "$line" >&2
    . <(jq <<< "$line" -r --arg self "$syncplay_nick" '
        def irc($msg): @sh "irc_say \($msg)";
        (.State // empty
            | @sh "syncplay_send \"{State: {}}\""),
        (.Chat // empty
            | select(.username != $self)
            | irc("<\(.username)> \(.message)")),
        (.Set // empty
            | .user // empty
            | keys[0] as $nick
            | .[$nick]
            | (
                (.event // empty
                    | irc("\($nick) \(if .joined then "joined the room" else "left the room" end)")),
                (.file // empty
                    | irc("\($nick) is now playing \(.name)"))
            )
        )
        ')
done &

wait