Add experimental Twitch plays functionality

This commit is contained in:
Max Goodhart
2020-11-03 13:26:10 -08:00
parent e77ac9ba3a
commit d32e8a0f41
3 changed files with 76 additions and 1 deletions

View File

@@ -54,6 +54,10 @@ json-url = ["https://woke.net/api/streams.json"]
#interval = 60
#delay = 30
[twitch.vote]
#template = "Switching to #<%- selectedIdx %> (with <%- voteCount %> votes)"
#interval = 15
[cert]
# SSL certificates (optional)
# If you specify an https:// URL for the "webserver" option, a certificate will be automatically generated and signed by Let's Encrypt.

View File

@@ -4,10 +4,12 @@ import ejs from 'ejs'
import { State } from 'xstate'
import { ChatClient, SlowModeRateLimiter, LoginError } from 'dank-twitch-irc'
const VOTE_RE = /^!(\d+)$/
export default class TwitchBot extends EventEmitter {
constructor(config) {
super()
const { username, token } = config
const { username, token, vote } = config
this.config = config
this.announceTemplate = ejs.compile(config.announce.template)
const client = new ChatClient({
@@ -23,6 +25,12 @@ export default class TwitchBot extends EventEmitter {
this.dwellTimeout = null
this.announceTimeouts = new Map()
if (vote.interval) {
this.voteTemplate = ejs.compile(config.vote.template)
this.votes = new Map()
setInterval(this.tallyVotes.bind(this), vote.interval * 1000)
}
client.on('ready', () => {
this.onReady()
})
@@ -38,6 +46,9 @@ export default class TwitchBot extends EventEmitter {
console.error('Twitch bot disconnected due to error:', err)
}
})
client.on('PRIVMSG', (msg) => {
this.onMsg(msg)
})
}
connect() {
@@ -105,4 +116,50 @@ export default class TwitchBot extends EventEmitter {
}, announce.interval * 1000)
this.announceTimeouts.set(listeningURL, timeout)
}
async tallyVotes() {
const { client } = this
const { channel } = this.config
if (this.votes.size === 0) {
return
}
let voteCount = 0
let selectedIdx = null
for (const [idx, value] of this.votes) {
if (value > voteCount) {
voteCount = value
selectedIdx = idx
}
}
const msg = this.voteTemplate({ selectedIdx, voteCount })
await client.say(channel, msg)
// Index spaces starting with 1
this.emit('setListeningView', selectedIdx - 1)
this.votes = new Map()
}
onMsg(msg) {
const { grid, vote } = this.config
if (!vote.interval) {
return
}
const match = msg.messageText.match(VOTE_RE)
if (!match) {
return
}
let idx
try {
idx = Number(match[1])
} catch (err) {
return
}
this.votes.set(idx, (this.votes.get(idx) || 0) + 1)
}
}

View File

@@ -98,6 +98,8 @@ function parseArgs() {
'twitch.color',
'twitch.announce.template',
'twitch.announce.interval',
'twitch.vote.template',
'twitch.vote.interval',
],
'Twitch Chat',
)
@@ -133,6 +135,15 @@ function parseArgs() {
number: true,
default: 30,
})
.option('twitch.vote.template', {
describe: 'Message template for vote result announcements',
default: 'Switching to #<%- selectedIdx %> (with <%- voteCount %> votes)',
})
.option('twitch.vote.interval', {
describe: 'Time interval (in seconds) between votes (0 to disable)',
number: true,
default: 0,
})
.group(
[
'control.username',
@@ -382,6 +393,9 @@ async function main() {
if (argv.twitch.token) {
twitchBot = new TwitchBot(argv.twitch)
twitchBot.on('setListeningView', (idx) => {
streamWindow.setListeningView(idx)
})
twitchBot.connect()
}