├── .gitignore
├── public
├── js
│ ├── following.js
│ ├── script.js
│ └── first.js
└── css
│ └── style.css
├── views
├── first.ejs
├── following.ejs
└── layout.ejs
├── README.md
├── package.json
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/public/js/following.js:
--------------------------------------------------------------------------------
1 | const getDataFromAPI = (name) =>
2 | (window.location.href = `https://www.twitchdatabase.com/following/${name}`);
3 |
--------------------------------------------------------------------------------
/views/first.ejs:
--------------------------------------------------------------------------------
1 |
Enter a name to see their first followers...
2 |
3 |
--------------------------------------------------------------------------------
/views/following.ejs:
--------------------------------------------------------------------------------
1 | Enter a name to see who they're following...
2 |
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Twitch Following
2 | Simple web app that shows you who Twitch users are following.
3 |
4 | # Running
5 | - Run `npm i` to install dependencies.
6 | - Run `npm start` to start the web server.
7 | - Open https://localhost:8081 to visit the web app.
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "twitch-following",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ."
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "ejs": "^3.1.5",
14 | "express": "^4.17.1",
15 | "express-ejs-layouts": "^2.5.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/public/js/script.js:
--------------------------------------------------------------------------------
1 | const input = document.querySelector('input');
2 | const resultDiv = document.querySelector('.result-div');
3 |
4 | const clientID = 'cclk5hafv1i7lksfauerry4w7ythu2';
5 |
6 | let currentlySearching = false;
7 |
8 | let starterName = location.search.substring(1);
9 |
10 | if (starterName) {
11 | input.value = starterName.toLowerCase();
12 | history.pushState(null, null, `?${input.value.toLowerCase()}`);
13 | getDataFromAPI(starterName);
14 | }
15 |
16 | input.onkeydown = event => {
17 | if (event.which === 13) {
18 | input.value = input.value.toLowerCase();
19 | history.pushState(null, null, `?${input.value.toLowerCase()}`);
20 | getDataFromAPI(input.value);
21 | }
22 | }
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | min-height: 100%;
3 | }
4 |
5 | body {
6 | position: absolute;
7 | width: 100%;
8 | padding-bottom: 286px;
9 | }
10 |
11 | body, input {
12 | font-family: 'Open Sans', sans-serif !important;
13 | }
14 |
15 | a {
16 | text-decoration: none !important;
17 | }
18 |
19 | .bg-black {
20 | background-color: #000;
21 | }
22 |
23 | .bg-purple {
24 | background-color: #9146FF;
25 | }
26 |
27 | .bg-grey {
28 | background-color: #0E0E10;
29 | }
30 |
31 | .text-purple, .text-purple:hover {
32 | color: #9146FF;
33 | }
34 |
35 | .ad {
36 | position: absolute;
37 | bottom: 50px;
38 | width: 100%;
39 | height: 286px;
40 | }
41 |
42 | .footer {
43 | position: absolute;
44 | bottom: 0;
45 | width: 100%;
46 | height: 50px;
47 | line-height: 50px;
48 | }
49 |
50 | .following-card {
51 | transition: opacity 1s, transform 0.2s;
52 | }
53 |
54 | .following-card:hover {
55 | transform: scale(1.05);
56 | }
57 |
58 | .first-card p {
59 | margin: 0;
60 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const expressLayouts = require('express-ejs-layouts');
3 |
4 | const app = express();
5 |
6 | app.set('view engine', 'ejs');
7 |
8 | app.use(expressLayouts);
9 |
10 | app.use(express.static('public'));
11 |
12 | const featuredChannels = ['streamcanvas'];
13 |
14 | app.use((req, res, next) => {
15 | res.locals.showFirstFollowersButton = new Date() < new Date('02/20/2022');
16 | next();
17 | });
18 |
19 | app.get('/', (req, res) => {
20 | res.render('following', {
21 | tool: 'following',
22 | featuredChannel:
23 | featuredChannels[Math.floor(Math.random() * featuredChannels.length)],
24 | });
25 | });
26 |
27 | app.use((req, res, next) => {
28 | if (new Date() > new Date('02/20/2022')) {
29 | res.redirect('https://www.twitchdatabase.com');
30 | } else {
31 | next();
32 | }
33 | });
34 |
35 | app.get('/first', (req, res) => {
36 | res.render('first', {
37 | tool: 'first',
38 | featuredChannel:
39 | featuredChannels[Math.floor(Math.random() * featuredChannels.length)],
40 | });
41 | });
42 |
43 | app.listen(8083);
44 |
--------------------------------------------------------------------------------
/public/js/first.js:
--------------------------------------------------------------------------------
1 | const getDataFromAPI = name => {
2 | resultDiv.innerHTML = `Looking for ${name}'s Twitch ID...
`;
3 | fetch(`https://api.twitch.tv/kraken/users?login=${name}`, {
4 | headers: {
5 | 'Accept': 'application/vnd.twitchtv.v5+json',
6 | 'Client-ID': clientID
7 | }
8 | })
9 | .then(res => res.json())
10 | .then(usersData => {
11 | if (usersData && usersData.users && usersData.users[0]) {
12 | const user = usersData.users[0];
13 | resultDiv.innerHTML = `Looking for ${user.display_name}'s first followers...`;
14 | fetch(`https://api.twitch.tv/kraken/channels/${user._id}/follows?limit=100&direction=asc`, {
15 | headers: {
16 | 'Accept': 'application/vnd.twitchtv.v5+json',
17 | 'Client-ID': clientID
18 | }
19 | })
20 | .then(res => res.json())
21 | .then(firstFollowers => {
22 | resultDiv.innerHTML = `
${user.display_name}'s first ${firstFollowers.follows.length} followers:
`;
23 | resultDiv.insertAdjacentHTML('beforeend', `
24 | `);
41 | firstFollowers.follows.forEach((follower, index) => {
42 | resultDiv.insertAdjacentHTML('beforeend', `
43 |
44 |
45 |
46 |
47 |
50 |
53 |
54 |
${follower.created_at.replace('T', ' ').replace('Z', '')}
55 |
56 |
57 |
58 |
59 |
`);
60 | });
61 | });
62 | } else {
63 | resultDiv.innerHTML = `Couldn't find that user. Did you type it right?
`;
64 | }
65 | });
66 | };
--------------------------------------------------------------------------------
/views/layout.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Twitch Following
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
49 |
50 | <%- body %>
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
--------------------------------------------------------------------------------