├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE.txt ├── Procfile ├── README.md ├── app.json ├── app ├── package.json ├── public │ ├── card.png │ ├── favicon.png │ ├── index.html │ └── robots.txt ├── src │ ├── components │ │ ├── App.tsx │ │ ├── Event.tsx │ │ ├── EventList.tsx │ │ ├── EventVisualization.tsx │ │ ├── Footer.tsx │ │ ├── Masthead.tsx │ │ ├── MusicGenerator.tsx │ │ └── ToggleModeButton.tsx │ ├── index.tsx │ ├── react-app-env.d.ts │ ├── react-reveal.d.ts │ ├── serviceWorker.ts │ └── theme.ts └── tsconfig.json ├── package.json ├── server ├── package.json ├── src │ ├── config.ts │ └── index.ts └── tsconfig.json └── yarn.lock /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | - run: yarn install --frozen-lockfile 14 | - run: yarn build 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | .DS_Store 4 | .env 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Victor Truong 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 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: yarn start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | iora logo 3 |

4 |

5 | Listen to music generated from new tweets on Twitter. 6 |

7 | 8 | ![iora screenshot](https://files.ifvictr.com/2020/08/iora_screenshot.png) 9 | 10 | Iora is inspired by [@debugger22](https://github.com/debugger22)’s [GitHub Audio](https://github.com/debugger22/github-audio). I had it playing in the background at one point while working on this. 😄 11 | 12 | ## How it works 13 | 14 | Each incoming tweet is converted into a note and duration pair. There are five different types a tweet can be classified as: new tweet, retweet, reply, poll, and media (images, videos, GIFs), and a note is assigned according to that. The duration is calculated by dividing the tweet length by 70 (the amount of characters per fourth of a tweet at max length). 15 | 16 | ## Deploy 17 | 18 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 19 | 20 | ## Setup 21 | 22 | Iora is composed of three components: 23 | 24 | 1. A **Twitter app** registered through the Twitter Developer Portal, which is used to access data from the platform 25 | 2. A **Node.js WebSockets server** which interfaces with Twitter’s stream API via a long-lived HTTP connection. Received messages are re-broadcasted to all connected clients as WebSocket messages. This is necessary because Twitter restricts apps to one concurrent connection. 26 | 3. A **React.js frontend** for music generation and displaying received data 27 | 28 | ### Creating the Twitter app 29 | 30 | 1. Go to Twitter’s [Developer Portal](https://developer.twitter.com/en/portal/dashboard) and create a new app. Make sure the app is compatible with Twitter’s API V2. 31 | 2. Go to **Keys and tokens** and note down the value of **Bearer token**. 32 | 33 | ### Environment variables 34 | 35 | Here are all the variables you need to set up on the server, with hints. 36 | 37 | ```bash 38 | # Port to run the server on. 39 | PORT=3000 40 | 41 | # The URL to prepend to all built assets that'll be served. 42 | PUBLIC_URL=https://iora.live 43 | # Obtained from the Twitter Developer Portal. 44 | TWITTER_BEARER_TOKEN=AAAA… 45 | ``` 46 | 47 | ### Starting the server 48 | 49 | _This section is only relevent to you if you’ve decided to run Iora on a platform other than Heroku._ 50 | 51 | ```bash 52 | git clone https://github.com/ifvictr/iora 53 | cd iora 54 | # Install dependencies 55 | yarn 56 | # Start Iora in production! This will build the source files and then run them. 57 | yarn build 58 | yarn start 59 | # Or, if you need to run it in development mode instead. This will start both the backend and frontend and run them concurrently. 60 | yarn dev 61 | ``` 62 | 63 | After you’ve followed all the above steps, you should see something like this in the console: 64 | 65 | ```bash 66 | Starting Iora… 67 | Listening on port 3000 68 | Connected to Twitter stream 69 | ``` 70 | 71 | ## License 72 | 73 | [MIT License](LICENSE.txt) 74 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iora", 3 | "description": "Listen to music generated from new tweets on Twitter", 4 | "logo": "https://files.ifvictr.com/2020/08/iora.png", 5 | "repository": "https://github.com/ifvictr/iora", 6 | "buildpacks": [ 7 | { 8 | "url": "heroku/nodejs" 9 | } 10 | ], 11 | "env": { 12 | "PUBLIC_URL": { 13 | "description": "The URL to serve the React app’s resources from. Will be prepended to all built assets.", 14 | "required": false 15 | }, 16 | "TWITTER_BEARER_TOKEN": { 17 | "description": "Bearer token obtained from an app created through Twitter’s Developer portal. Must have V2 access." 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@iora/app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "react-scripts build", 8 | "dev": "react-scripts start", 9 | "eject": "react-scripts eject", 10 | "test": "react-scripts test" 11 | }, 12 | "browserslist": { 13 | "production": [ 14 | ">0.2%", 15 | "not dead", 16 | "not op_mini all" 17 | ], 18 | "development": [ 19 | "last 1 chrome version", 20 | "last 1 firefox version", 21 | "last 1 safari version" 22 | ] 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "dependencies": { 28 | "d3": "^5.16.0", 29 | "react": "^16.13.1", 30 | "react-dom": "^16.13.1", 31 | "react-reveal": "^1.2.2", 32 | "react-scripts": "3.4.3", 33 | "theme-ui": "^0.3.1", 34 | "tone": "^14.7.39", 35 | "use-socketio": "^2.0.3" 36 | }, 37 | "devDependencies": { 38 | "@types/d3": "^5.7.2", 39 | "@types/node": "^14.0.27", 40 | "@types/react": "^16.9.46", 41 | "@types/react-dom": "^16.9.8", 42 | "@types/theme-ui": "^0.3.6", 43 | "typescript": "^3.9.7" 44 | }, 45 | "proxy": "http://localhost:3001" 46 | } 47 | -------------------------------------------------------------------------------- /app/public/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/iora/c53e8385080e121457aa748d5d89b40a58c9ceff/app/public/card.png -------------------------------------------------------------------------------- /app/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifvictr/iora/c53e8385080e121457aa748d5d89b40a58c9ceff/app/public/favicon.png -------------------------------------------------------------------------------- /app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | iora: Listen to music generated from new tweets on Twitter 10 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Container, ThemeProvider } from 'theme-ui' 3 | import { SocketIOProvider } from 'use-socketio' 4 | import theme from '../theme' 5 | import EventList from './EventList' 6 | import EventVisualization from './EventVisualization' 7 | import Footer from './Footer' 8 | import Masthead from './Masthead' 9 | import MusicGenerator from './MusicGenerator' 10 | import ToggleModeButton from './ToggleModeButton' 11 | 12 | const App = () => { 13 | return ( 14 | 15 | 22 | 23 | 24 |