├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .replit ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── assets ├── gh-light.png ├── index.html ├── progressbar.min.js ├── style.css ├── updater_functions.js └── visual_functions.js ├── common.js ├── config-default.js ├── discord.js ├── irc.js ├── package.json ├── server.js └── start.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["transform-runtime", { 4 | "polyfill": false, 5 | "regenerator": true 6 | }] 7 | ] 8 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "parserOptions": { 7 | "ecmaVersion": 2017, 8 | "sourceType":"module" 9 | }, 10 | "rules": { 11 | "indent": [ 12 | "error", 13 | "tab" 14 | ], 15 | "linebreak-style": [ 16 | "error", 17 | "unix" 18 | ], 19 | "quotes": [ 20 | "error", 21 | "double" 22 | ], 23 | "semi": [ 24 | "error", 25 | "always" 26 | ], 27 | "no-console": [ 28 | "off" 29 | ], 30 | "no-unused-vars": [ 31 | "warn" 32 | ] 33 | } 34 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | config.js 3 | *.pem 4 | data.json* 5 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "nodejs" 2 | run = "test -f config.js || cp config-default.js config.js && sed -i 's/80/1080/g' config.js && node start.js" 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}/start.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 rb28z2 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Maintainability](https://api.codeclimate.com/v1/badges/5245737267d0831fbc1d/maintainability)](https://codeclimate.com/github/rb28z2/progress-bot/maintainability) 2 | [![Run on Repl.it](https://repl.it/badge/github/rb28z2/progress-bot)](https://repl.it/github/rb28z2/progress-bot) 3 | 4 | # progress-bot 5 | High-tech weaponized moe progress delivery bot for IRC, Discord, and web. Uses NodeJS and socket.io to update progress from IRC/Discord messages onto a web interface. The web interface can also be embedded on another page to provide instant and live progress updates. 6 | 7 | # Screenshots 8 | ![web interface](http://i.imgur.com/nKWdaGL.gif) 9 | 10 | # Installation 11 | ##### Requirements 12 | * nodejs 13 | * npm 14 | * build-essential 15 | * libicu-dev 16 | 17 | ##### Instructions 18 | Install `build-essential` and `libicu-dev` via your package manager, then run `npm install` to install the required dependencies. Then, copy `config-default.js` to `config.js` and edit as required. Finally, run the bot with `node start.js`. 19 | 20 | ##### Compatibility 21 | In theory, this should work anywhere node (and the above dependencies) works. However I've only tested it on Linux (Ubuntu 16.04), and Windows 10. 22 | 23 | # Usage 24 | ##### Available commands and syntax 25 | 26 | Assuming a trigger word of `!pb`: 27 | 28 | 29 | | Command | Syntax | Example | 30 | | ------------- | ----------------- | ------------------------ | 31 | | Title | `title ` | `!pb title A very cool show` | 32 | | Episode | `episode ` | `!pb episode 12/25` | 33 | | Encoding | `encode ` | `!pb encode 30` | 34 | | Timing | `time ` | `!pb time 10` | 35 | | Translation | `tl ` | `!pb tl 100` | 36 | | Translation Check | `tlc ` | `!pb tlc 20` | 37 | | Typesetting | `ts ` | `!pb ts 44` | 38 | | Editing | `edit ` | `!pb edit 0` | 39 | | Quality Check | `qc ` | `!pb qc 67` | 40 | 41 | Open a browser to `:80` (port 80 by default) to see the live changes. 42 | 43 | # Demo (front end only) 44 | 45 | See a live, in-production setup here: https://asenshi.moe:8443 46 | -------------------------------------------------------------------------------- /assets/gh-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rb28z2/progress-bot/1d235f8f3b0108532052beca61076a4d8ed0f393/assets/gh-light.png -------------------------------------------------------------------------------- /assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Progress Bot 6 | 7 | 8 | 9 | 10 | 11 |
12 |
    13 |
    14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 | 28 |
    29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/progressbar.min.js: -------------------------------------------------------------------------------- 1 | // ProgressBar.js 1.0.1 2 | // https://kimmobrunfeldt.github.io/progressbar.js 3 | // License: MIT 4 | 5 | !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.ProgressBar=a()}}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};a[g][0].call(k.exports,function(b){var c=a[g][1][b];return e(c?c:b)},k,k.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;ga?0:(a-f)/e;for(h in b)b.hasOwnProperty(h)&&(i=g[h],k="function"==typeof i?i:o[i],b[h]=j(c[h],d[h],k,l));return b}function j(a,b,c,d){return a+(b-a)*c(d)}function k(a,b){var c=n.prototype.filter,d=a._filterArgs;f(c,function(e){"undefined"!=typeof c[e][b]&&c[e][b].apply(a,d)})}function l(a,b,c,d,e,f,g,h,j,l,m){v=b+c+d,w=Math.min(m||u(),v),x=w>=v,y=d-(v-w),a.isPlaying()&&(x?(j(g,a._attachment,y),a.stop(!0)):(a._scheduleId=l(a._timeoutHandler,s),k(a,"beforeTween"),b+c>w?i(1,e,f,g,1,1,h):i(w,e,f,g,d,b+c,h),k(a,"afterTween"),j(e,a._attachment,y)))}function m(a,b){var c={},d=typeof b;return"string"===d||"function"===d?f(a,function(a){c[a]=b}):f(a,function(a){c[a]||(c[a]=b[a]||q)}),c}function n(a,b){this._currentState=a||{},this._configured=!1,this._scheduleFunction=p,"undefined"!=typeof b&&this.setConfig(b)}var o,p,q="linear",r=500,s=1e3/60,t=Date.now?Date.now:function(){return+new Date},u="undefined"!=typeof SHIFTY_DEBUG_NOW?SHIFTY_DEBUG_NOW:t;p="undefined"!=typeof window?window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||window.mozCancelRequestAnimationFrame&&window.mozRequestAnimationFrame||setTimeout:setTimeout;var v,w,x,y;return n.prototype.tween=function(a){return this._isTweening?this:(void 0===a&&this._configured||this.setConfig(a),this._timestamp=u(),this._start(this.get(),this._attachment),this.resume())},n.prototype.setConfig=function(a){a=a||{},this._configured=!0,this._attachment=a.attachment,this._pausedAtTime=null,this._scheduleId=null,this._delay=a.delay||0,this._start=a.start||e,this._step=a.step||e,this._finish=a.finish||e,this._duration=a.duration||r,this._currentState=g({},a.from)||this.get(),this._originalState=this.get(),this._targetState=g({},a.to)||this.get();var b=this;this._timeoutHandler=function(){l(b,b._timestamp,b._delay,b._duration,b._currentState,b._originalState,b._targetState,b._easing,b._step,b._scheduleFunction)};var c=this._currentState,d=this._targetState;return h(d,c),this._easing=m(c,a.easing||q),this._filterArgs=[c,this._originalState,d,this._easing],k(this,"tweenCreated"),this},n.prototype.get=function(){return g({},this._currentState)},n.prototype.set=function(a){this._currentState=a},n.prototype.pause=function(){return this._pausedAtTime=u(),this._isPaused=!0,this},n.prototype.resume=function(){return this._isPaused&&(this._timestamp+=u()-this._pausedAtTime),this._isPaused=!1,this._isTweening=!0,this._timeoutHandler(),this},n.prototype.seek=function(a){a=Math.max(a,0);var b=u();return this._timestamp+a===0?this:(this._timestamp=b-a,this.isPlaying()||(this._isTweening=!0,this._isPaused=!1,l(this,this._timestamp,this._delay,this._duration,this._currentState,this._originalState,this._targetState,this._easing,this._step,this._scheduleFunction,b),this.pause()),this)},n.prototype.stop=function(a){return this._isTweening=!1,this._isPaused=!1,this._timeoutHandler=e,(b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.oCancelAnimationFrame||b.msCancelAnimationFrame||b.mozCancelRequestAnimationFrame||b.clearTimeout)(this._scheduleId),a&&(k(this,"beforeTween"),i(1,this._currentState,this._originalState,this._targetState,1,0,this._easing),k(this,"afterTween"),k(this,"afterTweenEnd"),this._finish.call(this,this._currentState,this._attachment)),this},n.prototype.isPlaying=function(){return this._isTweening&&!this._isPaused},n.prototype.setScheduleFunction=function(a){this._scheduleFunction=a},n.prototype.dispose=function(){var a;for(a in this)this.hasOwnProperty(a)&&delete this[a]},n.prototype.filter={},n.prototype.formula={linear:function(a){return a}},o=n.prototype.formula,g(n,{now:u,each:f,tweenProps:i,tweenProp:j,applyFilter:k,shallowCopy:g,defaults:h,composeEasingObject:m}),"function"==typeof SHIFTY_DEBUG_NOW&&(b.timeoutHandler=l),"object"==typeof d?c.exports=n:"function"==typeof a&&a.amd?a(function(){return n}):"undefined"==typeof b.Tweenable&&(b.Tweenable=n),n}();!function(){e.shallowCopy(e.prototype.formula,{easeInQuad:function(a){return Math.pow(a,2)},easeOutQuad:function(a){return-(Math.pow(a-1,2)-1)},easeInOutQuad:function(a){return(a/=.5)<1?.5*Math.pow(a,2):-.5*((a-=2)*a-2)},easeInCubic:function(a){return Math.pow(a,3)},easeOutCubic:function(a){return Math.pow(a-1,3)+1},easeInOutCubic:function(a){return(a/=.5)<1?.5*Math.pow(a,3):.5*(Math.pow(a-2,3)+2)},easeInQuart:function(a){return Math.pow(a,4)},easeOutQuart:function(a){return-(Math.pow(a-1,4)-1)},easeInOutQuart:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeInQuint:function(a){return Math.pow(a,5)},easeOutQuint:function(a){return Math.pow(a-1,5)+1},easeInOutQuint:function(a){return(a/=.5)<1?.5*Math.pow(a,5):.5*(Math.pow(a-2,5)+2)},easeInSine:function(a){return-Math.cos(a*(Math.PI/2))+1},easeOutSine:function(a){return Math.sin(a*(Math.PI/2))},easeInOutSine:function(a){return-.5*(Math.cos(Math.PI*a)-1)},easeInExpo:function(a){return 0===a?0:Math.pow(2,10*(a-1))},easeOutExpo:function(a){return 1===a?1:-Math.pow(2,-10*a)+1},easeInOutExpo:function(a){return 0===a?0:1===a?1:(a/=.5)<1?.5*Math.pow(2,10*(a-1)):.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return-(Math.sqrt(1-a*a)-1)},easeOutCirc:function(a){return Math.sqrt(1-Math.pow(a-1,2))},easeInOutCirc:function(a){return(a/=.5)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},easeOutBounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},easeInBack:function(a){var b=1.70158;return a*a*((b+1)*a-b)},easeOutBack:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},easeInOutBack:function(a){var b=1.70158;return(a/=.5)<1?.5*(a*a*(((b*=1.525)+1)*a-b)):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},elastic:function(a){return-1*Math.pow(4,-8*a)*Math.sin((6*a-1)*(2*Math.PI)/2)+1},swingFromTo:function(a){var b=1.70158;return(a/=.5)<1?.5*(a*a*(((b*=1.525)+1)*a-b)):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},swingFrom:function(a){var b=1.70158;return a*a*((b+1)*a-b)},swingTo:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},bounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},bouncePast:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?2-(7.5625*(a-=1.5/2.75)*a+.75):2.5/2.75>a?2-(7.5625*(a-=2.25/2.75)*a+.9375):2-(7.5625*(a-=2.625/2.75)*a+.984375)},easeFromTo:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeFrom:function(a){return Math.pow(a,4)},easeTo:function(a){return Math.pow(a,.25)}})}(),function(){function a(a,b,c,d,e,f){function g(a){return((n*a+o)*a+p)*a}function h(a){return((q*a+r)*a+s)*a}function i(a){return(3*n*a+2*o)*a+p}function j(a){return 1/(200*a)}function k(a,b){return h(m(a,b))}function l(a){return a>=0?a:0-a}function m(a,b){var c,d,e,f,h,j;for(e=a,j=0;8>j;j++){if(f=g(e)-a,l(f)e)return c;if(e>d)return d;for(;d>c;){if(f=g(e),l(f-a)f?c=e:d=e,e=.5*(d-c)+c}return e}var n=0,o=0,p=0,q=0,r=0,s=0;return p=3*b,o=3*(d-b)-p,n=1-p-o,s=3*c,r=3*(e-c)-s,q=1-s-r,k(a,j(f))}function b(b,c,d,e){return function(f){return a(f,b,c,d,e,1)}}e.setBezierFunction=function(a,c,d,f,g){var h=b(c,d,f,g);return h.displayName=a,h.x1=c,h.y1=d,h.x2=f,h.y2=g,e.prototype.formula[a]=h},e.unsetBezierFunction=function(a){delete e.prototype.formula[a]}}(),function(){function a(a,b,c,d,f,g){return e.tweenProps(d,b,a,c,1,g,f)}var b=new e;b._filterArgs=[],e.interpolate=function(c,d,f,g,h){var i=e.shallowCopy({},c),j=h||0,k=e.composeEasingObject(c,g||"linear");b.set({});var l=b._filterArgs;l.length=0,l[0]=i,l[1]=c,l[2]=d,l[3]=k,e.applyFilter(b,"tweenCreated"),e.applyFilter(b,"beforeTween");var m=a(c,i,d,f,k,j);return e.applyFilter(b,"afterTween"),m}}(),function(a){function b(a,b){var c,d=[],e=a.length;for(c=0;e>c;c++)d.push("_"+b+"_"+c);return d}function c(a){var b=a.match(v);return b?(1===b.length||a[0].match(u))&&b.unshift(""):b=["",""],b.join(A)}function d(b){a.each(b,function(a){var c=b[a];"string"==typeof c&&c.match(z)&&(b[a]=e(c))})}function e(a){return i(z,a,f)}function f(a){var b=g(a);return"rgb("+b[0]+","+b[1]+","+b[2]+")"}function g(a){return a=a.replace(/#/,""),3===a.length&&(a=a.split(""),a=a[0]+a[0]+a[1]+a[1]+a[2]+a[2]),B[0]=h(a.substr(0,2)),B[1]=h(a.substr(2,2)),B[2]=h(a.substr(4,2)),B}function h(a){return parseInt(a,16)}function i(a,b,c){var d=b.match(a),e=b.replace(a,A);if(d)for(var f,g=d.length,h=0;g>h;h++)f=d.shift(),e=e.replace(A,c(f));return e}function j(a){return i(x,a,k)}function k(a){for(var b=a.match(w),c=b.length,d=a.match(y)[0],e=0;c>e;e++)d+=parseInt(b[e],10)+",";return d=d.slice(0,-1)+")"}function l(d){var e={};return a.each(d,function(a){var f=d[a];if("string"==typeof f){var g=r(f);e[a]={formatString:c(f),chunkNames:b(g,a)}}}),e}function m(b,c){a.each(c,function(a){for(var d=b[a],e=r(d),f=e.length,g=0;f>g;g++)b[c[a].chunkNames[g]]=+e[g];delete b[a]})}function n(b,c){a.each(c,function(a){var d=b[a],e=o(b,c[a].chunkNames),f=p(e,c[a].chunkNames);d=q(c[a].formatString,f),b[a]=j(d)})}function o(a,b){for(var c,d={},e=b.length,f=0;e>f;f++)c=b[f],d[c]=a[c],delete a[c];return d}function p(a,b){C.length=0;for(var c=b.length,d=0;c>d;d++)C.push(a[b[d]]);return C}function q(a,b){for(var c=a,d=b.length,e=0;d>e;e++)c=c.replace(A,+b[e].toFixed(4));return c}function r(a){return a.match(w)}function s(b,c){a.each(c,function(a){var d,e=c[a],f=e.chunkNames,g=f.length,h=b[a];if("string"==typeof h){var i=h.split(" "),j=i[i.length-1];for(d=0;g>d;d++)b[f[d]]=i[d]||j}else for(d=0;g>d;d++)b[f[d]]=h;delete b[a]})}function t(b,c){a.each(c,function(a){var d=c[a],e=d.chunkNames,f=e.length,g=b[e[0]],h=typeof g;if("string"===h){for(var i="",j=0;f>j;j++)i+=" "+b[e[j]],delete b[e[j]];b[a]=i.substr(1)}else b[a]=g})}var u=/(\d|\-|\.)/,v=/([^\-0-9\.]+)/g,w=/[0-9.\-]+/g,x=new RegExp("rgb\\("+w.source+/,\s*/.source+w.source+/,\s*/.source+w.source+"\\)","g"),y=/^.*\(/,z=/#([0-9]|[a-f]){3,6}/gi,A="VAL",B=[],C=[];a.prototype.filter.token={tweenCreated:function(a,b,c,e){d(a),d(b),d(c),this._tokenData=l(a)},beforeTween:function(a,b,c,d){s(d,this._tokenData),m(a,this._tokenData),m(b,this._tokenData),m(c,this._tokenData)},afterTween:function(a,b,c,d){n(a,this._tokenData),n(b,this._tokenData),n(c,this._tokenData),t(d,this._tokenData)}}}(e)}).call(null)},{}],2:[function(a,b,c){var d=a("./shape"),e=a("./utils"),f=function(a,b){this._pathTemplate="M 50,50 m 0,-{radius} a {radius},{radius} 0 1 1 0,{2radius} a {radius},{radius} 0 1 1 0,-{2radius}",this.containerAspectRatio=1,d.apply(this,arguments)};f.prototype=new d,f.prototype.constructor=f,f.prototype._pathString=function(a){var b=a.strokeWidth;a.trailWidth&&a.trailWidth>a.strokeWidth&&(b=a.trailWidth);var c=50-b/2;return e.render(this._pathTemplate,{radius:c,"2radius":2*c})},f.prototype._trailString=function(a){return this._pathString(a)},b.exports=f},{"./shape":7,"./utils":8}],3:[function(a,b,c){var d=a("./shape"),e=a("./utils"),f=function(a,b){this._pathTemplate="M 0,{center} L 100,{center}",d.apply(this,arguments)};f.prototype=new d,f.prototype.constructor=f,f.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 "+b.strokeWidth),a.setAttribute("preserveAspectRatio","none")},f.prototype._pathString=function(a){return e.render(this._pathTemplate,{center:a.strokeWidth/2})},f.prototype._trailString=function(a){return this._pathString(a)},b.exports=f},{"./shape":7,"./utils":8}],4:[function(a,b,c){b.exports={Line:a("./line"),Circle:a("./circle"),SemiCircle:a("./semicircle"),Path:a("./path"),Shape:a("./shape"),utils:a("./utils")}},{"./circle":2,"./line":3,"./path":5,"./semicircle":6,"./shape":7,"./utils":8}],5:[function(a,b,c){var d=a("shifty"),e=a("./utils"),f={easeIn:"easeInCubic",easeOut:"easeOutCubic",easeInOut:"easeInOutCubic"},g=function h(a,b){if(!(this instanceof h))throw new Error("Constructor was called without new keyword");b=e.extend({duration:800,easing:"linear",from:{},to:{},step:function(){}},b);var c;c=e.isString(a)?document.querySelector(a):a,this.path=c,this._opts=b,this._tweenable=null;var d=this.path.getTotalLength();this.path.style.strokeDasharray=d+" "+d,this.set(0)};g.prototype.value=function(){var a=this._getComputedDashOffset(),b=this.path.getTotalLength(),c=1-a/b;return parseFloat(c.toFixed(6),10)},g.prototype.set=function(a){this.stop(),this.path.style.strokeDashoffset=this._progressToOffset(a);var b=this._opts.step;if(e.isFunction(b)){var c=this._easing(this._opts.easing),d=this._calculateTo(a,c),f=this._opts.shape||this;b(d,f,this._opts.attachment)}},g.prototype.stop=function(){this._stopTween(),this.path.style.strokeDashoffset=this._getComputedDashOffset()},g.prototype.animate=function(a,b,c){b=b||{},e.isFunction(b)&&(c=b,b={});var f=e.extend({},b),g=e.extend({},this._opts);b=e.extend(g,b);var h=this._easing(b.easing),i=this._resolveFromAndTo(a,h,f);this.stop(),this.path.getBoundingClientRect();var j=this._getComputedDashOffset(),k=this._progressToOffset(a),l=this;this._tweenable=new d,this._tweenable.tween({from:e.extend({offset:j},i.from),to:e.extend({offset:k},i.to),duration:b.duration,easing:h,step:function(a){l.path.style.strokeDashoffset=a.offset;var c=b.shape||l;b.step(a,c,b.attachment)},finish:function(a){e.isFunction(c)&&c()}})},g.prototype._getComputedDashOffset=function(){var a=window.getComputedStyle(this.path,null);return parseFloat(a.getPropertyValue("stroke-dashoffset"),10)},g.prototype._progressToOffset=function(a){var b=this.path.getTotalLength();return b-a*b},g.prototype._resolveFromAndTo=function(a,b,c){return c.from&&c.to?{from:c.from,to:c.to}:{from:this._calculateFrom(b),to:this._calculateTo(a,b)}},g.prototype._calculateFrom=function(a){return d.interpolate(this._opts.from,this._opts.to,this.value(),a)},g.prototype._calculateTo=function(a,b){return d.interpolate(this._opts.from,this._opts.to,a,b)},g.prototype._stopTween=function(){null!==this._tweenable&&(this._tweenable.stop(),this._tweenable=null)},g.prototype._easing=function(a){return f.hasOwnProperty(a)?f[a]:a},b.exports=g},{"./utils":8,shifty:1}],6:[function(a,b,c){var d=a("./shape"),e=a("./circle"),f=a("./utils"),g=function(a,b){this._pathTemplate="M 50,50 m -{radius},0 a {radius},{radius} 0 1 1 {2radius},0",this.containerAspectRatio=2,d.apply(this,arguments)};g.prototype=new d,g.prototype.constructor=g,g.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 50")},g.prototype._initializeTextContainer=function(a,b,c){a.text.style&&(c.style.top="auto",c.style.bottom="0",a.text.alignToBottom?f.setStyle(c,"transform","translate(-50%, 0)"):f.setStyle(c,"transform","translate(-50%, 50%)"))},g.prototype._pathString=e.prototype._pathString,g.prototype._trailString=e.prototype._trailString,b.exports=g},{"./circle":2,"./shape":7,"./utils":8}],7:[function(a,b,c){var d=a("./path"),e=a("./utils"),f="Object is destroyed",g=function h(a,b){if(!(this instanceof h))throw new Error("Constructor was called without new keyword");if(0!==arguments.length){this._opts=e.extend({color:"#555",strokeWidth:1,trailColor:null,trailWidth:null,fill:null,text:{style:{color:null,position:"absolute",left:"50%",top:"50%",padding:0,margin:0,transform:{prefix:!0,value:"translate(-50%, -50%)"}},autoStyleContainer:!0,alignToBottom:!0,value:null,className:"progressbar-text"},svgStyle:{display:"block",width:"100%"},warnings:!1},b,!0),e.isObject(b)&&void 0!==b.svgStyle&&(this._opts.svgStyle=b.svgStyle),e.isObject(b)&&e.isObject(b.text)&&void 0!==b.text.style&&(this._opts.text.style=b.text.style);var c,f=this._createSvgView(this._opts);if(c=e.isString(a)?document.querySelector(a):a,!c)throw new Error("Container does not exist: "+a);this._container=c,this._container.appendChild(f.svg),this._opts.warnings&&this._warnContainerAspectRatio(this._container),this._opts.svgStyle&&e.setStyles(f.svg,this._opts.svgStyle),this.svg=f.svg,this.path=f.path,this.trail=f.trail,this.text=null;var g=e.extend({attachment:void 0,shape:this},this._opts);this._progressPath=new d(f.path,g),e.isObject(this._opts.text)&&null!==this._opts.text.value&&this.setText(this._opts.text.value)}};g.prototype.animate=function(a,b,c){if(null===this._progressPath)throw new Error(f);this._progressPath.animate(a,b,c)},g.prototype.stop=function(){if(null===this._progressPath)throw new Error(f);void 0!==this._progressPath&&this._progressPath.stop()},g.prototype.destroy=function(){if(null===this._progressPath)throw new Error(f);this.stop(),this.svg.parentNode.removeChild(this.svg),this.svg=null,this.path=null,this.trail=null,this._progressPath=null,null!==this.text&&(this.text.parentNode.removeChild(this.text),this.text=null)},g.prototype.set=function(a){if(null===this._progressPath)throw new Error(f);this._progressPath.set(a)},g.prototype.value=function(){if(null===this._progressPath)throw new Error(f);return void 0===this._progressPath?0:this._progressPath.value()},g.prototype.setText=function(a){if(null===this._progressPath)throw new Error(f);null===this.text&&(this.text=this._createTextContainer(this._opts,this._container),this._container.appendChild(this.text)),e.isObject(a)?(e.removeChildren(this.text),this.text.appendChild(a)):this.text.innerHTML=a},g.prototype._createSvgView=function(a){var b=document.createElementNS("http://www.w3.org/2000/svg","svg");this._initializeSvg(b,a);var c=null;(a.trailColor||a.trailWidth)&&(c=this._createTrail(a),b.appendChild(c));var d=this._createPath(a);return b.appendChild(d),{svg:b,path:d,trail:c}},g.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 100")},g.prototype._createPath=function(a){var b=this._pathString(a);return this._createPathElement(b,a)},g.prototype._createTrail=function(a){var b=this._trailString(a),c=e.extend({},a);return c.trailColor||(c.trailColor="#eee"),c.trailWidth||(c.trailWidth=c.strokeWidth),c.color=c.trailColor,c.strokeWidth=c.trailWidth,c.fill=null,this._createPathElement(b,c)},g.prototype._createPathElement=function(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg","path");return c.setAttribute("d",a),c.setAttribute("stroke",b.color),c.setAttribute("stroke-width",b.strokeWidth),b.fill?c.setAttribute("fill",b.fill):c.setAttribute("fill-opacity","0"),c},g.prototype._createTextContainer=function(a,b){var c=document.createElement("div");c.className=a.text.className;var d=a.text.style;return d&&(a.text.autoStyleContainer&&(b.style.position="relative"),e.setStyles(c,d),d.color||(c.style.color=a.color)),this._initializeTextContainer(a,b,c),c},g.prototype._initializeTextContainer=function(a,b,c){},g.prototype._pathString=function(a){throw new Error("Override this function for each progress bar")},g.prototype._trailString=function(a){throw new Error("Override this function for each progress bar")},g.prototype._warnContainerAspectRatio=function(a){if(this.containerAspectRatio){var b=window.getComputedStyle(a,null),c=parseFloat(b.getPropertyValue("width"),10),d=parseFloat(b.getPropertyValue("height"),10);e.floatEquals(this.containerAspectRatio,c/d)||(console.warn("Incorrect aspect ratio of container","#"+a.id,"detected:",b.getPropertyValue("width")+"(width)","/",b.getPropertyValue("height")+"(height)","=",c/d),console.warn("Aspect ratio of should be",this.containerAspectRatio))}},b.exports=g},{"./path":5,"./utils":8}],8:[function(a,b,c){function d(a,b,c){a=a||{},b=b||{},c=c||!1;for(var e in b)if(b.hasOwnProperty(e)){var f=a[e],g=b[e];c&&l(f)&&l(g)?a[e]=d(f,g,c):a[e]=g}return a}function e(a,b){var c=a;for(var d in b)if(b.hasOwnProperty(d)){var e=b[d],f="\\{"+d+"\\}",g=new RegExp(f,"g");c=c.replace(g,e)}return c}function f(a,b,c){for(var d=a.style,e=0;e img { 67 | opacity: 0.7; 68 | width: 15px; 69 | } 70 | 71 | #footer { 72 | margin-top: -5px; 73 | } 74 | 75 | body { 76 | background: #292929; 77 | } 78 | -------------------------------------------------------------------------------- /assets/updater_functions.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | var socket = io(); 3 | 4 | console.log("Starting"); 5 | var stats = {}; 6 | //var [encode], [title], [episode], [time], [tl], [ts], [edit], [qc]; 7 | 8 | 9 | socket.on("init-stats", function(val) { 10 | if (val["command"] !== "title" && val["command"] !== "episode") { 11 | //console.log(val["command"]); 12 | 13 | var dispCommand = ""; 14 | if (val["command"] === "tl") 15 | dispCommand = "Translation"; 16 | else if (val["command"] === "edit") 17 | dispCommand = "Editing"; 18 | else if (val["command"] === "ts") 19 | dispCommand = "Typesetting"; 20 | else if (val["command"] === "time") 21 | dispCommand = "Timing"; 22 | else if (val["command"] === "encode") 23 | dispCommand = "Encoding"; 24 | else if (val["command"] === "tlc") 25 | dispCommand = "TLC"; 26 | else if (val["command"] === "qc") 27 | dispCommand = "QC"; 28 | 29 | if (!stats[val["command"]]) { 30 | stats[val["command"]] = new ProgressBar.Line("#pb-" + val["command"], { 31 | easing: "easeInOut", 32 | color: "#287fb8", 33 | trailColor: "#555", 34 | trailWidth: 1, 35 | svgStyle: { 36 | width: "100%", 37 | height: "50%" 38 | }, 39 | text: { 40 | style: { 41 | autoStyleContainer: false, 42 | color: "#fff", 43 | fontFamily: "\"Open Sans\", Helvetica, Arial, sans-serif", 44 | fontSize: "3.4vh", 45 | position: "absolute", 46 | width: "100%", 47 | textAlign: "center", 48 | fontWeight: "600", 49 | top: "0%" 50 | } 51 | }, 52 | step: (state, bar) => { 53 | bar.setText(dispCommand + ": " + Math.round(bar.value() * 100) + "%"); 54 | } 55 | }); 56 | } 57 | stats[val["command"]].animate(val["value"]); 58 | } else { 59 | console.log(val["value"]); 60 | if (val["command"] === "title") { 61 | $("#pb-title").text(val["value"]); 62 | } else { 63 | $("#pb-episode").text("Episode: " + val["value"]); 64 | } 65 | } 66 | }); 67 | 68 | 69 | socket.on("update-stats", function(val) { 70 | console.log("Updating"); 71 | if (val["command"] !== "title" && val["command"] !== "episode") { 72 | stats[val["command"]].animate(val["value"] / 100); 73 | } else { 74 | if (val["command"] === "title") { 75 | $("#pb-title").text(val["value"]); 76 | } else { 77 | $("#pb-episode").text("Episode: " + val["value"]); 78 | } 79 | } 80 | 81 | }); 82 | 83 | socket.on("update-users", function(val) { 84 | $("#totalUsers").text("Users: " + val); 85 | }); 86 | 87 | socket.on("date-update", function(val) { 88 | $("#update").text(val); 89 | }); -------------------------------------------------------------------------------- /assets/visual_functions.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | $("#plug").hover(function() 3 | { 4 | console.log("Hover"); 5 | $("#plug-text").fadeIn(); 6 | }, function() 7 | { 8 | $("#plug-text").stop().delay(3000).fadeOut(); 9 | } 10 | ); 11 | 12 | var original; 13 | 14 | $("#pb-qc").hover(function() 15 | { 16 | original = $("#pb-qc > div").text(); 17 | $("#pb-qc > div").text("Quality Checking"); 18 | }, function() 19 | { 20 | $("#pb-qc > div").text(original); 21 | } 22 | ); 23 | 24 | $("#pb-tlc").hover(function() 25 | { 26 | original = $("#pb-tlc > div").text(); 27 | $("#pb-tlc > div").text("Translation Checking"); 28 | }, function() 29 | { 30 | $("#pb-tlc > div").text(original); 31 | } 32 | ); -------------------------------------------------------------------------------- /common.js: -------------------------------------------------------------------------------- 1 | import jsonFile from "jsonfile"; 2 | import config from "./config.js"; 3 | let ioInstance; 4 | let stats; 5 | 6 | const encode = "encode"; 7 | const title = "title"; 8 | const tlc = "tlc"; 9 | const episode = "episode"; 10 | const time = "time"; 11 | const tl = "tl"; 12 | const ts = "ts"; 13 | const edit = "edit"; 14 | const qc = "qc"; 15 | 16 | export function initIo(http) { 17 | ioInstance = require("socket.io")(http); 18 | return ioInstance; 19 | } 20 | 21 | export function io() { 22 | return ioInstance; 23 | } 24 | 25 | export var lastUpdated = new Date().toUTCString(); 26 | export var validCommands = ["encode", "tlc", "title", "episode", "time", "tl", "ts", "edit", "qc"]; 27 | 28 | export function getStats() { 29 | if (!stats){ 30 | console.log("Reading existing data...".green); 31 | const file = `${__dirname}/data.json`; 32 | try { 33 | stats = jsonFile.readFileSync(file); 34 | } 35 | catch (err) { 36 | if (err.code === "ENOENT") { 37 | //If no data file was found, start with dummy data 38 | console.log("No default data file found".yellow); 39 | console.log("Creating dummy data".yellow); 40 | stats = { 41 | "encode": 0, 42 | "title": "Another show", 43 | "episode": "5/12", 44 | "time": "20", 45 | "tl": "50", 46 | "ts": 0, 47 | "edit": "50", 48 | "qc": "60", 49 | "tlc": "20" 50 | }; 51 | } 52 | } 53 | } 54 | return stats; 55 | } 56 | 57 | export var file = `${__dirname}/data.json`; 58 | 59 | export function saveStats() { 60 | jsonFile.writeFileSync(file, getStats()); 61 | console.log("Saving stats to disk".yellow); 62 | } 63 | 64 | setInterval(saveStats, 1000 * 60 * 10); // save stats every 10 minutes 65 | 66 | export function triggerMatch(text) { 67 | return text.substring(0, config.trigger.length) === config.trigger; 68 | } 69 | 70 | export function getMsg(text) { 71 | return text.substring(config.trigger.length); 72 | } 73 | 74 | export function getCommand(msg) { 75 | return msg.substring(0, msg.indexOf(" ")); 76 | } 77 | 78 | export function getValue(msg) { 79 | return msg.substring(msg.indexOf(" ") + 1); 80 | } 81 | 82 | export function newTitleTrigger(command, value) { 83 | const tempTitle = stats["title"]; 84 | if (command === "title" || command === "episode") { 85 | console.log("Resetting everything".yellow); 86 | for (const key in stats) { 87 | if (stats.hasOwnProperty(key)) { 88 | stats[key] = 0; 89 | ioInstance.emit("update-stats", { 90 | "command": key, 91 | "value": 0 92 | }); 93 | } 94 | } 95 | 96 | if (command === "episode") { 97 | stats["title"] = tempTitle; 98 | stats["episode"] = value; 99 | ioInstance.emit("update-stats", { 100 | "command": "title", 101 | "value": tempTitle 102 | }); 103 | } 104 | } 105 | 106 | } 107 | 108 | export function getIRCtoSay(command) { 109 | if (command === "episode") { 110 | return `Currently working on \u0002${stats[title]}\u0002 episode ${stats[episode]}`; 111 | } 112 | else if (command !== "title") { 113 | return `\u0002${stats[title]}\u0002 | Episode ${stats[episode]} | ${capitalizeFirst(command)} progress @ ${stats[command]}%`; 114 | } 115 | else return null; 116 | } 117 | 118 | export function getDiscordtoSay(command) { 119 | if (command === "episode") { 120 | return `Currently working on **${stats[title]}** episode ${stats[episode]}`; 121 | } 122 | else if (command !== "title") { 123 | return `**${stats[title]}** | Episode ${stats[episode]} | ${capitalizeFirst(command)} progress @ ${stats[command]}%`; 124 | } 125 | else return null; 126 | } 127 | 128 | function capitalizeFirst(string) { 129 | if (string.length > 3) { 130 | return string.charAt(0).toUpperCase() + string.slice(1); 131 | } 132 | else { 133 | return string.toUpperCase(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /config-default.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | enableIrc: true, 3 | /* 4 | List all channels the bot should join, include channel keys if required. 5 | ex: ["#channelA", "#channelB channelB-password", "#anotherChannel"] 6 | */ 7 | channels: ["#yourchannel"], 8 | /* 9 | List of channels (a subset of 'channels') that the bot should listen for commands on. 10 | Note that everyone in that channel will be able to trigger commands. 11 | */ 12 | listenChannel: ["#yourchannel"], 13 | /* 14 | List of channels (a subset of 'channels') that the bot should announce updates on. 15 | Bot will NOT respond to trigger commands here. 16 | */ 17 | notifyChannel: ["#yourchannel"], 18 | server: "irc.server.here", 19 | botName: "progressBot", 20 | port: 80, 21 | httpsMode: false, //enables https only mode 22 | httpsPort: 8443, 23 | httpsKey: "/path/to/key.pem", //port, key, and cert not required in http mode 24 | httpsCert: "/path/to/cert.pem", 25 | trigger: "!pb ", //Word to trigger actions. IMPORTANT: INCLUDE A TRAILING SPACE 26 | identify: false, //Set to true to enable nickserv identification 27 | nick_secret: false, //set to a "" enclosed password if you dont want to enter it every time 28 | // else leave false to prompt for a password 29 | nickserv: "nickserv", //nick identification service's name 30 | 31 | 32 | enableDiscord: false, 33 | discordKey: "yourkeyhere", // your discord bot token 34 | discordNotifyChannels: ["0"], // comma separated numerical notify channel id 35 | discordListenChannels: ["0"] 36 | }; 37 | 38 | export default config; 39 | -------------------------------------------------------------------------------- /discord.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | import colors from "colors"; 3 | 4 | import Discord from "discord.js"; 5 | const client = new Discord.Client(); 6 | import config from "./config.js"; 7 | import { io as _io, triggerMatch, getMsg, getCommand, getValue, validCommands, newTitleTrigger, getStats, getDiscordtoSay, getIRCtoSay } from "./common.js"; 8 | import { ircSay } from "./irc.js"; 9 | 10 | const stats = getStats(); 11 | const io = _io(); 12 | 13 | 14 | export function initDiscord() { 15 | client.on("ready", () => { 16 | console.log(`Logged in as ${client.user.tag}`.yellow); 17 | }); 18 | 19 | let lastUpdated = exports.lastUpdated; 20 | 21 | client.on("message", msg => { 22 | let authenticated = false; 23 | 24 | if (config.discordListenChannels.includes(msg.channel.id)) 25 | authenticated = true; 26 | 27 | if (triggerMatch(msg.content) && authenticated) { 28 | const message = getMsg(msg.content); 29 | const command = getCommand(message); 30 | const value = getValue(message); 31 | 32 | if (validCommands.includes(command)) { 33 | newTitleTrigger(command, value); 34 | console.log("Valid command: ".yellow, command, value); 35 | stats[command] = value; 36 | 37 | let discordMessage = getDiscordtoSay(command); 38 | let ircMessage = getIRCtoSay(command); 39 | if (config.enableDiscord && discordMessage) discordSay(discordMessage); 40 | if (config.enableIrc && ircMessage) ircSay(ircMessage); 41 | 42 | 43 | io.emit("update-stats", { 44 | "command": command, 45 | "value": value 46 | }); 47 | lastUpdated = new Date().toUTCString(); 48 | io.emit("date-update", lastUpdated); 49 | } 50 | } 51 | }); 52 | 53 | client.on("error", error => { 54 | console.log(error); 55 | }); 56 | 57 | client.login(config.discordKey); 58 | } 59 | 60 | export function discordSay(message) { 61 | config.discordNotifyChannels.forEach( async value => { 62 | let channel = client.channels.get(value); 63 | await channel.send(message); 64 | }); 65 | } -------------------------------------------------------------------------------- /irc.js: -------------------------------------------------------------------------------- 1 | import irc from "irc"; 2 | import config from "./config.js"; 3 | import { io as _io, getStats, validCommands, getCommand, getValue, triggerMatch, getMsg, newTitleTrigger, getIRCtoSay, getDiscordtoSay} from "./common.js"; 4 | import { discordSay } from "./discord.js"; 5 | 6 | const io = _io(); 7 | const stats = getStats(); 8 | let bot; 9 | 10 | export function initIRC() { 11 | console.log("Connecting to IRC...".green); 12 | bot = new irc.Client(config.server, config.botName, { 13 | channels: config.channels 14 | }); 15 | console.log("Connected!".yellow); 16 | 17 | authorize(); 18 | 19 | let lastUpdated = exports.lastUpdated; 20 | 21 | const listener = `message${config.listenChannel[0]}`; 22 | 23 | console.log("Adding listener for trigger...".green); 24 | /** 25 | * Below block is for listening to a specific trigger word. 26 | */ 27 | bot.addListener(listener, (from, text, message) => { 28 | //extract the first n characters from each message and check if it matches the trigger word 29 | if (triggerMatch(text)) { 30 | const msg = getMsg(text); 31 | console.log("Message Received: ", msg); 32 | //if we have a matching trigger, extract the command the value 33 | const command = getCommand(msg); 34 | const value = getValue(msg); 35 | 36 | 37 | if (validCommands.includes(command)) { 38 | 39 | newTitleTrigger(command, value); 40 | console.log("Valid command: ".yellow, command, value); 41 | stats[command] = value; 42 | 43 | let ircMessage = getIRCtoSay(command); 44 | let discordMessage = getDiscordtoSay(command); 45 | 46 | if (config.enableDiscord && discordMessage) ircSay(ircMessage); 47 | if (config.enableIrc && ircMessage) discordSay(discordMessage); 48 | } 49 | 50 | io.emit("update-stats", { 51 | "command": command, 52 | "value": value 53 | }); 54 | lastUpdated = new Date().toUTCString(); 55 | io.emit("date-update", lastUpdated); 56 | 57 | } 58 | }); 59 | 60 | bot.addListener("error", message => { 61 | console.log("IRC Error ".red, message); 62 | }); 63 | 64 | 65 | 66 | async function authorize() { 67 | if (config.identify) { 68 | console.log("Identify nick enabled".yellow); 69 | if (config.nick_secret) { 70 | console.log("Password found".green); 71 | let password = config.nick_secret; 72 | bot.say(config.nickserv, `identify ${password}`); 73 | } 74 | else { 75 | console.log("Prompting for password".yellow); 76 | let pass_prompt = require("password-prompt"); 77 | let password = await pass_prompt("ENTER PASSWORD AT ANY TIME"); 78 | bot.say(config.nickserv, `identify ${password}`); 79 | } 80 | console.log("Identified".green); //todo: check if identify was successful 81 | } 82 | } 83 | } 84 | 85 | export function ircSay(message) { 86 | if (message){ 87 | for (let i = 0; i < config.notifyChannel.length; i++) { 88 | bot.say(config.notifyChannel[i], message); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progressBot", 3 | "version": "0.0.1", 4 | "description": "hi-tech weaponized moe progress delivery", 5 | "dependencies": { 6 | "colors": "^1.1.2", 7 | "discord.js": "^11.4.2", 8 | "express": "^4.15.2", 9 | "irc": "^0.5.2", 10 | "jsonfile": "^6.0.1", 11 | "password-prompt": "^1.1.2", 12 | "socket.io": "^2.0.1" 13 | }, 14 | "devDependencies": { 15 | "babel-plugin-transform-runtime": "^6.23.0", 16 | "babel-preset-env": "^1.7.0", 17 | "babel-register": "^6.26.0", 18 | "eslint": "^6.7.2", 19 | "nodemon": "^2.0.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import config from "./config.js"; 2 | import fs from "fs"; 3 | 4 | //below block is to init stuff. nothing special here 5 | import colors from "colors"; 6 | 7 | console.log("Initializing Express and HTTP stuff...".green); 8 | import express from "express"; 9 | 10 | const app = express(); 11 | app.use(express.static("assets")); //deliver content in the 'assets' folder 12 | 13 | let http = require("http") 14 | .Server(app); 15 | 16 | import {getStats, initIo, lastUpdated, validCommands, file } from "./common.js"; 17 | let stats = getStats(); 18 | 19 | 20 | //configure in HTTPS mode 21 | if (config.httpsMode) { 22 | console.log("HTTPS Mode".yellow); 23 | http = require("https"); 24 | http = http.createServer({ 25 | key: fs.readFileSync(config.httpsKey), 26 | cert: fs.readFileSync(config.httpsCert) 27 | }, app); 28 | } 29 | 30 | console.log("Initializing Socket.io stuff...".green); 31 | 32 | const io = initIo(http); // socket.io for realtime stuff 33 | 34 | 35 | import jsonFile from "jsonfile"; //because i'm lazy 36 | 37 | if (config.enableIrc) { 38 | let ircClient = require("./irc.js"); 39 | ircClient.initIRC(); 40 | } 41 | 42 | if (config.enableDiscord) { 43 | let discordClient = require("./discord.js"); 44 | discordClient.initDiscord(); 45 | } 46 | 47 | console.log("\nINIT COMPLETE\n".bold.magenta); 48 | 49 | console.log(colors.grey("%s\n"), JSON.stringify(stats)); 50 | 51 | 52 | app.get("/", (req, res) => { 53 | res.sendFile(`${__dirname}/index.html`); 54 | }); 55 | 56 | app.get("/progressbar.min.js", (req, res) => { 57 | res.sendFile(`${__dirname}/progressbar.min.js`); 58 | }); 59 | 60 | io.on("connection", socket => { 61 | console.log("Socket connection established. ID: ".grey, socket.id); 62 | socket.emit("irc message", "Connected!"); 63 | 64 | socket.emit("date-update", lastUpdated); 65 | 66 | 67 | 68 | //for each new client, update their stats (initial update) 69 | for (let i = 0; i < validCommands.length; i++) { 70 | let command = validCommands[i]; 71 | //console.log(command); 72 | if (command !== "title" && command !== "episode") { 73 | socket.emit("init-stats", { 74 | "command": validCommands[i], 75 | "value": stats[validCommands[i]] / 100 76 | }); 77 | } 78 | else { 79 | socket.emit("init-stats", { 80 | "command": validCommands[i], 81 | "value": stats[validCommands[i]] 82 | }); 83 | } 84 | } 85 | 86 | io.emit("update-users", io.engine.clientsCount); 87 | 88 | socket.on("disconnect", socket => { 89 | io.emit("update-users", io.engine.clientsCount); 90 | }); 91 | 92 | }); 93 | 94 | 95 | if (config.httpsMode) { 96 | http.listen(8443, () => { 97 | console.log("Listening on port %s in HTTPS mode".bold.yellow, config.httpsPort); 98 | }); 99 | } 100 | else { 101 | http.listen(config.port, () => { 102 | console.log("Listening on port %s in HTTP mode".bold.yellow, config.port); 103 | }); 104 | } 105 | //Below stuff is all for clean exits and for uncaught exception handling 106 | process.stdin.resume(); //so the program will not close instantly 107 | 108 | function exitHandler({cleanup, exit}, err) { 109 | if (cleanup) console.log("clean".red); 110 | if (err) console.log(err.stack); 111 | 112 | jsonFile.writeFileSync(file, stats); 113 | console.log("Saving stats to disk".yellow); 114 | 115 | if (exit) process.exit(); 116 | } 117 | 118 | //do something when app is closing 119 | process.on("exit", exitHandler.bind(null, { 120 | cleanup: true 121 | })); 122 | 123 | //catches ctrl+c event 124 | process.on("SIGINT", exitHandler.bind(null, { 125 | exit: true 126 | })); 127 | 128 | //catches uncaught exceptions 129 | process.on("uncaughtException", exitHandler.bind(null, { 130 | exit: true 131 | })); 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /start.js: -------------------------------------------------------------------------------- 1 | // Transpile all code following this line with babel and use 'env' (aka ES6) preset. 2 | require("babel-register")({ 3 | presets: [ "env" ] 4 | }); 5 | 6 | // Import the rest of our application. 7 | module.exports = require("./server.js"); 8 | --------------------------------------------------------------------------------