Skip to content

Commit

Permalink
lib/api: Add chatters check on unofficial API
Browse files Browse the repository at this point in the history
  • Loading branch information
sogehige committed Apr 6, 2018
1 parent ab6ff67 commit e18a73f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 3 deletions.
45 changes: 45 additions & 0 deletions libs/api.js
Expand Up @@ -43,6 +43,8 @@ class API {
this.updateChannelViews()
this.getChannelHosts()

this.getChannelChattersUnofficialAPI()

this.getChannelSubscribersOldAPI() // remove this after twitch add total subscribers
this.getChannelDataOldAPI() // remove this after twitch game and status for new API

Expand Down Expand Up @@ -91,6 +93,49 @@ class API {
}
}

async getChannelChattersUnofficialAPI () {
const url = `https://tmi.twitch.tv/group/user/${config.settings.broadcaster_username.toLowerCase()}/chatters`
let timeout = 60000
var request
try {
request = await snekfetch.get(url)
global.db.engine.insert('APIStats', { timestamp: _.now(), call: 'getChannelChattersUnofficialAPI', api: 'unofficial', endpoint: url, code: request.status })
} catch (e) {
timeout = e.errno === 'ECONNREFUSED' || e.errno === 'ETIMEDOUT' ? 1000 : timeout
global.log.error(`${url} - ${e.message}`)
global.db.engine.insert('APIStats', { timestamp: _.now(), call: 'getChannelChattersUnofficialAPI', api: 'unofficial', endpoint: url, code: `${e.status} ${_.get(e, 'body.message', e.message)}` })
return
} finally {
if (timeout !== 0) setTimeout(() => this.getChannelChattersUnofficialAPI(), timeout)
}

const chatters = _.flatMap(request.body.chatters)
debug('api:getChannelChattersUnofficialAPI')(chatters)

for (let chatter of chatters) {
const isIgnored = !_.isEmpty(await global.db.engine.findOne('users_ignorelist', { username: chatter }))
if (!isIgnored) {
const user = await global.db.engine.findOne('users', { username: chatter })
if (!_.get(user, 'is.online', false)) {
global.api.isFollower(chatter)
global.users.set(chatter, { is: { online: true } })
global.widgets.joinpart.send({ username: chatter, type: 'join' })
global.events.fire('user-joined-channel', { username: chatter })
}
}
}

const allOnlineUsers = await global.db.engine.find('users', { is: { online: true } })
for (let user of allOnlineUsers) {
if (!_.includes(chatters, user.username)) {
// user is no longer in channel
global.users.set(user.username, { is: { online: false } })
global.widgets.joinpart.send({ username: user.username, type: 'part' })
global.events.fire('user-parted-channel', { username: user.username })
}
}
}

async getChannelSubscribersOldAPI () {
const cid = await global.cache.channelId()
const url = `https://api.twitch.tv/kraken/channels/${cid}/subscriptions?limit=100`
Expand Down
2 changes: 1 addition & 1 deletion libs/widgets/chat.js
Expand Up @@ -9,7 +9,7 @@ function ChatWidget () {
global.panel.socketListening(this, 'chat.message.send', this.chatMessageSend)

this.refresh()
setInterval(() => this.refresh(), 30000)
setInterval(() => this.refresh(), 60000)
}

ChatWidget.prototype.refresh = async (self, socket) => {
Expand Down
6 changes: 4 additions & 2 deletions main.js
Expand Up @@ -252,7 +252,8 @@ function loadClientListeners (client) {
let ignoredUser = await global.db.engine.findOne('users_ignorelist', { username: username })
if (!_.isEmpty(ignoredUser) && username !== config.settings.broadcaster_username) return

if (!fromSelf) {
const user = await global.db.engine.findOne('users', { username: username })
if (!fromSelf && !_.get(user, 'is.online', false)) {
global.api.isFollower(username)
global.users.set(username, { is: { online: true } })
global.widgets.joinpart.send({ username: username, type: 'join' })
Expand All @@ -266,7 +267,8 @@ function loadClientListeners (client) {
let ignoredUser = await global.db.engine.findOne('users_ignorelist', { username: username })
if (!_.isEmpty(ignoredUser) && username !== config.settings.broadcaster_username) return

if (!fromSelf) {
const user = await global.db.engine.findOne('users', { username: username })
if (!fromSelf && !_.get(user, 'is.online', true)) {
global.users.set(username, { is: { online: false } })
global.widgets.joinpart.send({ username: username, type: 'part' })
global.events.fire('user-parted-channel', { username: username })
Expand Down
75 changes: 75 additions & 0 deletions public/pages/apistats.html
Expand Up @@ -11,6 +11,9 @@
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#tmi">TMI</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#unofficial">UNOFFICIAL</a>
</li>
</ul>

<div class="tab-content">
Expand Down Expand Up @@ -63,6 +66,22 @@
</table>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="unofficial">
<div class="widget">
<canvas id="unofficialChart" width="400" height="100"></canvas>
<table class="table table-responsive">
<thead class="thead-dark">
<tr>
<th scope="col">time</th>
<th scope="col">call</th>
<th scope="col">endpoint</th>
<th scope="col">status</th>
</tr>
</thead>
<tbody class="unofficialData"></tbody>
</table>
</div>
</div>
</div>

<script>
Expand All @@ -73,14 +92,17 @@
var helixChart = new Chart(document.getElementById("helixChart").getContext('2d'), { type: 'line', options: { scales: { yAxes: [{ ticks: { beginAtZero:true, stepSize: 1 } }] } } })
var krakenChart = new Chart(document.getElementById("krakenChart").getContext('2d'), { type: 'line', options: { scales: { yAxes: [{ ticks: { beginAtZero:true, stepSize: 1 } }] } } })
var tmiChart = new Chart(document.getElementById("tmiChart").getContext('2d'), { type: 'line', options: { scales: { yAxes: [{ ticks: { beginAtZero:true, stepSize: 1 } }] } } })
var unofficialChart = new Chart(document.getElementById("unofficialChart").getContext('2d'), { type: 'line', options: { scales: { yAxes: [{ ticks: { beginAtZero:true, stepSize: 1 } }] } } })

$('.helixData').empty()
$('.krakenData').empty()
$('.tmiData').empty()
$('.unofficialChart').empty()

helixChart.clear()
krakenChart.clear()
tmiChart.clear()
unofficialChart.clear()

socket.off('api.stats')
socket.on('api.stats', (data) => {
Expand All @@ -107,13 +129,16 @@
const helixData = _.filter(data, (o) => o.api === 'helix')
const krakenData = _.filter(data, (o) => o.api === 'kraken')
const tmiData = _.filter(data, (o) => o.api === 'tmi')
const unofficialData = _.filter(data, (o) => o.api === 'unofficial')

let helixTimestampsPerMinute = {}
let helixTimestampsErrorsPerMinute = {}
let krakenTimestampsPerMinute = {}
let krakenTimestampsErrorsPerMinute = {}
let tmiTimestampsPerMinute = {}
let tmiTimestampsErrorsPerMinute = {}
let unofficialTimestampsPerMinute = {}
let unofficialTimestampsErrorsPerMinute = {}

let lastTimestampSaved = 0
for (let timestamp of _.map(helixData, 'timestamp')) {
Expand Down Expand Up @@ -184,6 +209,29 @@
tmiTimestampsErrorsPerMinute[index] = tmiTimestampsErrorsPerMinute[index].length
}

lastTimestampSaved = 0
for (let timestamp of _.map(unofficialData, 'timestamp')) {
// timestamps needs to be reversed as we have data from newest to oldest
if (timestamp >= lastTimestampSaved + 60000) {
lastTimestampSaved = timestamp
unofficialTimestampsPerMinute[lastTimestampSaved] = []
unofficialTimestampsErrorsPerMinute[lastTimestampSaved] = []
}

if (_.find(unofficialData, (o) => o.code !== 200 && o.timestamp === timestamp)) {
unofficialTimestampsErrorsPerMinute[lastTimestampSaved].push(timestamp)
}
unofficialTimestampsPerMinute[lastTimestampSaved].push(timestamp)
}

for (let index in unofficialTimestampsPerMinute) {
unofficialTimestampsPerMinute[index] = unofficialTimestampsPerMinute[index].length
}

for (let index in unofficialTimestampsErrorsPerMinute) {
unofficialTimestampsErrorsPerMinute[index] = unofficialTimestampsErrorsPerMinute[index].length
}

/* HELIX CHART DATA*/
for (let label of _.map(_.keys(helixTimestampsPerMinute), (o) => moment(parseInt(o, 10)).format('LTS'))) {
helixChart.data.labels.push(label)
Expand Down Expand Up @@ -289,6 +337,33 @@
} else tmiChart.data.datasets[1].data.push(count)
}
tmiChart.update()

/* UNOFFICIAL CHART DATA */$
for (let label of _.map(_.keys(unofficialTimestampsPerMinute), (o) => moment(parseInt(o, 10)).format('LTS'))) {
unofficialChart.data.labels.push(label)
}
for (let count of _.values(unofficialTimestampsPerMinute)) {
if (_.isNil(unofficialChart.data.datasets[0])) {
unofficialChart.data.datasets.push({
label: 'unofficial API calls',
data: [ count ],
backgroundColor: 'rgba(0, 0, 255, 0.2)',
borderColor: 'rgba(0, 0, 255, 0.5)'
})
} else unofficialChart.data.datasets[0].data.push(count)
}

for (let count of _.values(unofficialTimestampsErrorsPerMinute)) {
if (_.isNil(unofficialChart.data.datasets[1])) {
unofficialChart.data.datasets.push({
label: 'ERRORS',
data: [ count ],
backgroundColor: 'rgba(255, 0, 0, 0.2)',
borderColor: 'rgba(255, 0, 0, 0.5)',
})
} else unofficialChart.data.datasets[1].data.push(count)
}
unofficialChart.update()
})

</script>

0 comments on commit e18a73f

Please sign in to comment.