├── README.md ├── .gitignore ├── .babelrc ├── src ├── matrix-key.example.js ├── lastfm-key.example.js ├── ajax.js ├── simple-client.js └── lastfm-bot.js ├── .editorconfig ├── package.json ├── .eslintrc ├── server └── config └── webpack.dev.js /README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: ['es2015'], 3 | } 4 | -------------------------------------------------------------------------------- /src/matrix-key.example.js: -------------------------------------------------------------------------------- 1 | module.exports.userId = '@foobar:matrix.org'; 2 | module.exports.pass = 'xxx'; 3 | -------------------------------------------------------------------------------- /src/lastfm-key.example.js: -------------------------------------------------------------------------------- 1 | // get the api key from http://www.last.fm/api/account/create 2 | // you will need an last.fm account for that 3 | 4 | module.exports.lastFmAPIkey = 'xxx'; 5 | module.exports.lastFmSharedSecret = 'xxx'; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | # indent_size = 2 #not used since this should be up to everybody 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.lsg] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /src/ajax.js: -------------------------------------------------------------------------------- 1 | export default function ajax(url, callback) { 2 | const xmlhttp = new XMLHttpRequest(); 3 | xmlhttp.onreadystatechange = () => { 4 | if (xmlhttp.readyState === 4) { 5 | if (xmlhttp.status === 200) callback(JSON.parse(xmlhttp.responseText)); 6 | else console.warn('error loading ' + url); 7 | } 8 | }; 9 | xmlhttp.open('GET', url, true); 10 | xmlhttp.send(); 11 | } 12 | -------------------------------------------------------------------------------- /src/simple-client.js: -------------------------------------------------------------------------------- 1 | var sdk = require("matrix-js-sdk"); 2 | 3 | var matrixClient = sdk.createClient('https://matrix.org'); 4 | 5 | let joinedRoom = false; 6 | 7 | matrixClient.registerGuest().then((creds) => { 8 | const client = sdk.createClient({ 9 | baseUrl: 'https://matrix.org', 10 | accessToken: creds.access_token, 11 | userId: creds.user_id, 12 | deviceId: creds.device_id, 13 | guest: true, 14 | timelineSupport: true, 15 | }); 16 | client.on("Room.timeline", (event, room, toStartOfTimeline) => { 17 | console.log('event: ',event) 18 | }); 19 | client.on("sync", (event, data1, data2) => { 20 | if (!joinedRoom) { 21 | client.joinRoom('#dev-test-room:matrix.org').done((room) => { 22 | console.log('joined Room: ', room); 23 | joinedRoom = true; 24 | }); 25 | } 26 | }); 27 | client.setGuest(true); 28 | // client.startClient({initialSyncLimit: 20}); 29 | client.startClient(0); 30 | }); 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audius-matrix-bot", 3 | "version": "0.0.1", 4 | "description": "Listen to songs posted on matrix", 5 | "main": "index.js", 6 | "author": "select", 7 | "license": "MIT", 8 | "dependencies": { 9 | "cli-color": "^1.1.0", 10 | "matrix-js-sdk": "^0.7.2", 11 | "musicbrainz": "^0.2.6", 12 | "node-fetch": "^1.6.3" 13 | }, 14 | "devDependencies": { 15 | "autoprefixer": "^6.6.0", 16 | "babel-core": "^6.21.0", 17 | "babel-eslint": "^7.1.1", 18 | "babel-loader": "^6.2.10", 19 | "babel-preset-es2015": "^6.18.0", 20 | "circular-dependency-plugin": "^2.0.0", 21 | "copy-webpack-plugin": "^4.0.1", 22 | "eslint": "^3.12.2", 23 | "eslint-config-airbnb": "^13.0.0", 24 | "eslint-plugin-import": "^2.2.0", 25 | "eslint-plugin-jsx-a11y": "^2.2.3", 26 | "eslint-plugin-react": "^6.8.0", 27 | "webpack": "^1.14.0", 28 | "webpack-dev-server": "^1.16.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": ["error", "tab"], // indent with tabs 4 | "quotes": ["error", "single"], // always use single quotes 5 | "linebreak-style": ["error", "unix"], // fuck windows 6 | "semi": ["error", "always"], // always use semicolons at end of command 7 | "no-multiple-empty-lines": ["error", {"max": 2}], // only allow one free line 8 | "eqeqeq": ["error", "smart"], // always use === only if null or something not ... I forgot 9 | "strict": ["error", "function"], 10 | "no-var": ["error"], 11 | "max-len": ["error", 200, 4], 12 | "no-tabs": ["off"], 13 | "no-confusing-arrow": ["off"], 14 | "no-plusplus": ["off"] 15 | }, 16 | "ecmaFeatures": { 17 | "modules": true 18 | }, 19 | "globals": { 20 | "module": true, 21 | "chrome": true, 22 | }, 23 | "env": { 24 | "es6": true, 25 | "browser": true, 26 | "jasmine": true, 27 | "commonjs": true, 28 | }, 29 | "extends": "airbnb", 30 | "parser": "babel-eslint" // we need to use this since ESLint does not support ES6 Decorators 31 | } 32 | -------------------------------------------------------------------------------- /server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const WebpackDevServer = require('webpack-dev-server'); 4 | const webpack = require('webpack'); 5 | const CircularDependencyPlugin = require('circular-dependency-plugin'); 6 | 7 | const config = require('./config/webpack.dev.js'); 8 | 9 | config.entry = [ 10 | 'webpack-dev-server/client?http://localhost:8080/', 11 | 'webpack/hot/dev-server', 12 | './src/simple-client.js', 13 | ]; 14 | 15 | config.output = { 16 | path: '/build', 17 | publicPath: '/', 18 | filename: 'simple-client.js', 19 | }; 20 | 21 | config.plugins = [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': { 24 | website: true, 25 | }, 26 | }), 27 | new CircularDependencyPlugin(), 28 | new webpack.HotModuleReplacementPlugin(), 29 | ]; 30 | 31 | new WebpackDevServer(webpack(config), { 32 | contentBase: './src/static/', 33 | publicPath: config.output.publicPath, 34 | historyApiFallback: false, 35 | stats: { 36 | colors: true, 37 | chunks: false, 38 | }, 39 | hot: true, 40 | inline: true, 41 | }).listen(8080, 'localhost', function(err, res) { 42 | if (err) console.warn(err); 43 | console.log('Listening on localhost:8080'); 44 | }); 45 | -------------------------------------------------------------------------------- /src/lastfm-bot.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fetch = require('node-fetch'); 3 | const Matrix = require('matrix-js-sdk'); 4 | 5 | const lastFmAPIkey = require('./lastfm-key').lastFmAPIkey; 6 | const matrixLogin = require('./matrix-key'); 7 | 8 | const artistRexEx = /^!artist (.*)/; 9 | 10 | const matrixClient = Matrix.createClient('https://matrix.org'); 11 | matrixClient.loginWithPassword(matrixLogin.userId, matrixLogin.pass).then((credentials) => { 12 | console.log('login success'); 13 | const client = Matrix.createClient({ 14 | baseUrl: 'https://matrix.org', 15 | accessToken: credentials.access_token, 16 | userId: credentials.user_id, 17 | }); 18 | client.on('Room.timeline', (event, room, toStartOfTimeline) => { 19 | const message = event.event.content.body; 20 | // console.log('event: ',event) 21 | if (artistRexEx.test(message)) { 22 | const match = artistRexEx.exec(message); 23 | if (match) { 24 | fetch(`http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=${match[1]}&api_key=${lastFmAPIkey}&format=json`) 25 | .then(res => res.json()) 26 | .then((data) => { 27 | client.sendHtmlMessage( 28 | event.event.room_id, 29 | data.artist.bio.summary, 30 | data.artist.bio.summary, 31 | () => { 32 | console.log('###send: ', data.artist.bio.summary); 33 | } 34 | ); 35 | }); 36 | } 37 | } else { 38 | console.log('message did not match: ', message); 39 | } 40 | }); 41 | client.startClient(0); 42 | }); 43 | -------------------------------------------------------------------------------- /config/webpack.dev.js: -------------------------------------------------------------------------------- 1 | /* global __dirname */ 2 | 3 | const webpack = require('webpack'); 4 | const autoprefixer = require('autoprefixer'); 5 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 6 | const CircularDependencyPlugin = require('circular-dependency-plugin'); 7 | 8 | module.exports = { 9 | entry: { 10 | 'dist-extension/content-script': './src/extension/dev.js', 11 | }, 12 | output: { 13 | path: './', 14 | filename: '[name].js' 15 | }, 16 | stats: { 17 | // Configure the console output 18 | colors: false, 19 | modules: true, 20 | reasons: true, 21 | }, 22 | devtool: 'source-map', 23 | module: { 24 | loaders: [ 25 | // { test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$|\.wav$|\.mp3$/, loader: 'file' }, 26 | { test: /\.vue$/, loader: 'vue' }, 27 | { test: /\.ttf$/, loader: 'url-loader' }, 28 | { 29 | test: /\.sass$/, 30 | // loader: 'style!css?sourceMap!postcss?sourceMap!sass?sourceMap', 31 | loaders: ['style', 'css', 'postcss-loader', 'sass'], 32 | }, 33 | { test: /\.css$/, loader: 'raw-loader' }, 34 | { test: /\.html$/, loader: 'raw-loader' }, 35 | { test: /\.svg$/, loader: 'raw-loader' }, 36 | { 37 | test: /\.js$/, 38 | exclude: /node_modules/, 39 | loader: 'babel-loader', 40 | query: { 41 | cacheDirectory: true, 42 | presets: ['es2015'], 43 | }, 44 | }, 45 | ], 46 | }, 47 | postcss: () => [autoprefixer], 48 | plugins: [ 49 | new CopyWebpackPlugin([ 50 | { context: './src/extension/static/', from: '**/*', to: './dist-extension/' }, 51 | { context: './src/website/static/', from: '**/*', to: './dist-website/' }, 52 | ]), 53 | new webpack.DefinePlugin({ 54 | 'process.env': { 55 | extension: true, 56 | }, 57 | }), 58 | new CircularDependencyPlugin({ 59 | failOnError: true 60 | }), 61 | ], 62 | }; 63 | --------------------------------------------------------------------------------