- It can send notifications to only one webhook
- Data isn't persisted, which means that everytime this script (re)starts the last
Source String Update
event will be notified again - Notifications will not have a direct link to the specific updated strings due to Crowdin Activity API limitations
Last active
November 1, 2020 10:31
-
-
Save liddack/22a6f04f4813b53f55d832bd0faf85f3 to your computer and use it in GitHub Desktop.
Crowdin Source Update String Notifier for Discord Project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Loading logger module | |
const log = require('fancy-log') | |
log.info('Loading...') | |
// Loading the rest of the modules | |
const Webhook = require("./webhook"), | |
express = require('express'), | |
TurndownService = require('turndown'), | |
turndownService = new TurndownService(), | |
events = require('events'), | |
// This URL returns JSON data containing only 'Source String Update' events | |
CROWDIN_ACTIVITY_URL = "https://crowdin.com/project_actions/activity_stream?date_from=&date_to=&user_id=12799052&project_id=121045&language_id=0&type=3&translation_id=0&after_build=0&before_build=0&request=5", | |
CROWDIN_URL = 'https://crowdin.com/', | |
snekfetch = require('snekfetch'), | |
// Replace the placeholder below w/ your actual webhook URL | |
Hook = new Webhook("YOUR_WEBHOOK_URL"); | |
// Loading event emitter and catcher | |
var mediaEmitter = new events.EventEmitter(), | |
// Stores timestamp for the latest source string update event | |
previousTimestamp = 0; | |
// Add a rule to turn 'Discord' into a Markdown link to Discord's Crowdin profile | |
turndownService.addRule('profile-link', { | |
filter: (node, options) => { | |
return ( | |
options.linkStyle === 'inlined' && | |
node.nodeName === 'A' && | |
node.getAttribute('href') | |
) | |
}, | |
replacement: (content, node, options) => { | |
return '[' + content + ']('+ CROWDIN_URL + node.getAttribute('href') + ')' | |
} | |
}) | |
// Add a rule to bold all text between 'span' tags | |
// (aka. "Discord", "Discord Marketing" and "Discord API" project files) | |
turndownService.addRule('profile-link', { | |
filter: (node, options) => { | |
return ( | |
node.nodeName === 'SPAN' | |
) | |
}, | |
replacement: (content, node, options) => { | |
return `**${content}**` | |
} | |
}) | |
// Attempts to connect to Crowdin activity API | |
checkActivity = () => { | |
log.info("Checking activity... ") | |
snekfetch.get(CROWDIN_ACTIVITY_URL) | |
.then(function (res) { | |
mediaEmitter.emit('CONNECTED', res) | |
}) | |
.catch(function (err) { | |
mediaEmitter.emit('ERROR', err) | |
}) | |
} | |
// Emits an event when connection to Crowdin API was succesfull | |
mediaEmitter.on('CONNECTED', function (res) { | |
// Sends retrieved data to analyzeActivity() function | |
analyzeActivity(res.body) | |
}) | |
// Emits an event when an connection attempt to Crowdin fails | |
mediaEmitter.on('ERROR', function (code) { | |
log.error('ERRROR: ' + code) | |
}) | |
// App loaded successfully | |
log.info('App ready.') | |
// Do the first call to checkActivity() | |
checkActivity() | |
// Creates a loop to check activity every 2.5 minutes | |
// (1000 miliseconds * 60 seconds * 2.5 minutes) | |
global.intloop = setInterval(checkActivity, 1000 * 60 * 2.5) | |
// Gets activity JSON data, checks if there's a newer source string update | |
// and, if so, send a message to the specified webhook | |
analyzeActivity = (data) => { | |
let tempv = previousTimestamp; | |
for (let i = 0; i < data.activity.length; i++) { | |
if (data.activity[i].type == "update_project_files") { | |
let timestamp = data.activity[i].timestamp; | |
if (timestamp > previousTimestamp) { | |
previousTimestamp = timestamp; | |
let message = parseMessage(data.activity[i].message) | |
log.info('NEW SOURCE STRING UPDATE at ' + new Date(data.activity[i].timestamp * 1000) + ':\n' + message) | |
Hook.send('Crowdin Webhook', message, 'Source Strings Updated!', '#85ae52', timestamp) | |
} | |
} | |
} | |
if (tempv == previousTimestamp) log.info('Nothing yet.') | |
} | |
// Messages within the JSON data are written in HTML. This | |
// function converts it into a Discord-friendy Markdown text. | |
parseMessage = (message) => { | |
return turndownService.turndown(message) + | |
'\nhttps://crowdin.com/project/discord' | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file is basically a fork of https://github.com/JoeBanks13/webhook-discord | |
// tweaked to fullfill the specific needs of this app. | |
const snekfetch = require("snekfetch"); | |
const endpoint = "https://discordapp.com/api/v6/webhooks"; | |
class Webhook { | |
constructor(url) { | |
this.url = url; | |
this.id = ''; | |
this.token = ''; | |
this.meta = {}; | |
snekfetch.get(this.url) | |
.then((res) => { | |
let parsed = JSON.parse(res.text); | |
Object.assign(this.meta, parsed); | |
this.id = parsed['id']; | |
this.token = parsed['token']; | |
}) | |
.catch((err) => { | |
throw err; | |
}) | |
} | |
sendPayload(payload) { | |
return new Promise((resolve, reject) => { | |
snekfetch.post(this.endpoint) | |
.send(payload) | |
.then(() => { | |
resolve() | |
}) | |
.catch((err) => { | |
reject(err.text) | |
}) | |
}); | |
} | |
get endpoint() { | |
return `${endpoint}/${this.id}/${this.token}/slack` | |
} | |
send(name, message, title, color, timestamp){ | |
let payload; | |
payload = { | |
"username": name, | |
"text": "", | |
"attachments": [{ | |
"color": color, | |
"fields": [{ | |
"title": title, | |
"value": message | |
}], | |
"ts": timestamp | |
}] | |
} | |
return this.sendPayload(payload); | |
} | |
} | |
module.exports = Webhook; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How did you get CROWDIN_ACTIVITY_URL?