From 1d93f4e1602a4328010265feb0d71e84f5a36257 Mon Sep 17 00:00:00 2001 From: Xetera Date: Sun, 28 Jul 2019 20:02:47 -0700 Subject: [PATCH 1/8] callbacks post WIP --- src/content/docs/javascript/callbacks/intro.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/content/docs/javascript/callbacks/intro.md diff --git a/src/content/docs/javascript/callbacks/intro.md b/src/content/docs/javascript/callbacks/intro.md new file mode 100644 index 0000000..ae0a8c2 --- /dev/null +++ b/src/content/docs/javascript/callbacks/intro.md @@ -0,0 +1,10 @@ +--- +authors: + - "@Xetera" +created_at: 2019/07/27 +title: Callbacks +--- + +# Callbacks are everywhere + +If you've been doing javascript for any amount of time you'll have noticed that callbacks appear in just about every single piece of code. From 1b6525f4b455d72b0c1c6fb23d01480f6c09bc3e Mon Sep 17 00:00:00 2001 From: Xetera Date: Sun, 28 Jul 2019 21:16:24 -0700 Subject: [PATCH 2/8] added user-fetching functionality on build --- .gitignore | 2 + README.md | 7 +-- gatsby-node.js | 46 +++++++++++++------ package-lock.json | 34 ++++++++++++++ package.json | 1 + scripts/buildHelpers.js | 34 ++++++++++++++ scripts/fetchUsers.js | 42 +++++++++++++++++ src/components/DiscordButton/index.tsx | 14 ++++++ src/components/DocsSidebar/index.tsx | 3 +- .../docs/javascript/callbacks/intro.md | 10 ---- .../javascript/iterative-vs-functional.md | 2 +- src/content/docs/javascript/promises/intro.md | 2 +- .../promises/simplifying-promises.md | 2 +- .../docs/php/design-patterns/singleton.md | 2 +- src/content/docs/python/basics.md | 3 +- 15 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 scripts/buildHelpers.js create mode 100644 scripts/fetchUsers.js create mode 100644 src/components/DiscordButton/index.tsx delete mode 100644 src/content/docs/javascript/callbacks/intro.md diff --git a/.gitignore b/.gitignore index e86fe25..6a2ce6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +users.json + # Logs logs *.log diff --git a/README.md b/README.md index ab607ee..69057d1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ - + +# TPH Website + The official website for [The Programmer's Hangout](https://discord.gg/programming). ## Contributing @@ -40,7 +42,7 @@ Gatsby will take care of most of this process by creating a new page and positio 5. Run through the [setup steps](#setup) if possible to make sure your changes look ok. 6. Create a pull request. -#### Frontmatter +#### Frontmatter Frontmatter the optional metadata attached to every markdown file, like the list of authors or the creation date of the file. It is essentially just yaml syntax inside `---` delimiters within a markdown file. If you're not familiar with yaml you can find some refreshers [here](https://learnxinyminutes.com/docs/yaml/) @@ -62,4 +64,3 @@ some content here ``` You can look at other resources like [this one](/src/content/docs/javascript/promises/intro.md) for reference when creating your own. We use [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) as the date format to confuse both Americans and Europeans an equal amount. - diff --git a/gatsby-node.js b/gatsby-node.js index 0978f39..307e7e4 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -1,28 +1,40 @@ const { graphql } = require("gatsby") const { createFilePath } = require("gatsby-source-filesystem") +const helper = require("./scripts/buildHelpers") const path = require(`path`) +const fs = require("fs") -const usersEndpoint = id => `https://discordapp.com/api/guilds/${id}/members` +const requiredArticleFrontmatter = ["authors", "date"] -const fetchUsers = () => { - // Fetch user data from discord using bot token here +let users = [] +try { + users = JSON.parse(fs.readFileSync("./users.json", "utf-8")) + console.log("(◕◡◕✿) Users list loaded!") +} catch (e) { + console.log("(◕‸ ◕✿) Users list not present, skipping user resolution...") +} + +const validateResourceArticle = node => { + const missingField = requiredArticleFrontmatter.find( + required => node.frontmatter[required] + ) + if (missingField) { + console.error( + `Article ${node.fileAbsolutePath} is missing field '${missingField}'` + ) + process.exit(1) + } } const createDocs = async ({ createPage, graphql }) => { const languageDocs = path.resolve(`src/templates/languagePost.tsx`) const result = await graphql(` - { + query FetchDocs { allFile(filter: { sourceInstanceName: { eq: "docs" } }) { edges { node { relativePath - childMarkdownRemark { - frontmatter { - author - date - } - } } } } @@ -47,11 +59,19 @@ const createDocs = async ({ createPage, graphql }) => { exports.onCreateNode = async ({ node, getNode, actions }) => { const { createPage, createNodeField } = actions if (node.internal.type === "MarkdownRemark") { - const slug = createFilePath({ node, getNode, basePath: "src/content/docs" }) + const { frontmatter } = node + const isDoc = Boolean(!frontmatter.path) + + if (!isDoc) { + return + } + + const authors = helper.resolveAuthors(users, frontmatter.authors) + createNodeField({ node, - name: "slug", - value: slug, + name: "authors", + value: authors, }) } } diff --git a/package-lock.json b/package-lock.json index 9b1622e..3903ddb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5323,6 +5323,25 @@ } } }, + "discord.js": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.5.1.tgz", + "integrity": "sha512-tGhV5xaZXE3Z+4uXJb3hYM6gQ1NmnSxp9PClcsSAYFVRzH6AJH74040mO3afPDMWEAlj8XsoPXXTJHTxesqcGw==", + "requires": { + "long": "^4.0.0", + "prism-media": "^0.0.3", + "snekfetch": "^3.6.4", + "tweetnacl": "^1.0.0", + "ws": "^6.0.0" + }, + "dependencies": { + "tweetnacl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", + "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + } + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -10942,6 +10961,11 @@ "resolved": "https://registry.npmjs.org/lokijs/-/lokijs-1.5.7.tgz", "integrity": "sha512-2SqUV6JH4f15Z5/7LVsyadSUwHhZppxhujgy/VhVqiRYMGt5oaocb7fV/3JGjHJ6rTuEIajnpTLGRz9cJW/c3g==" }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -13580,6 +13604,11 @@ "utila": "~0.4" } }, + "prism-media": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.3.tgz", + "integrity": "sha512-c9KkNifSMU/iXT8FFTaBwBMr+rdVcN+H/uNv1o+CuFeTThNZNTOrQ+RgXA1yL/DeLk098duAeRPP3QNPNbhxYQ==" + }, "prismjs": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", @@ -15428,6 +15457,11 @@ } } }, + "snekfetch": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz", + "integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw==" + }, "socket.io": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz", diff --git a/package.json b/package.json index 26ddca5..075f3b2 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@types/ramda": "^0.26.18", "axios": "^0.19.0", "babel-plugin-styled-components": "^1.10.6", + "discord.js": "^11.5.1", "gatsby": "^2.13.39", "gatsby-image": "^2.2.6", "gatsby-plugin-manifest": "^2.2.4", diff --git a/scripts/buildHelpers.js b/scripts/buildHelpers.js new file mode 100644 index 0000000..ccb75eb --- /dev/null +++ b/scripts/buildHelpers.js @@ -0,0 +1,34 @@ +const DEFAULT_AVATAR_MODULO = 5 +module.exports = { + splitHash(identifier) { + const hashLocation = identifier.lastIndexOf("#") + + if (hashLocation === -1) { + throw Error(`Invalid name format ${identifier}`) + } + + const name = identifier.slice(0, hashLocation) + const hash = identifier.slice(hashLocation + 1) + return [name, hash] + }, + getDefaultAvatar(name) { + const [_, hash] = this.splitHash(hashLocation) + + const avatar = Number(hash) % DEFAULT_AVATAR_MODULO + return `https://cdn.discordapp.com/embed/avatars/${avatar}.png` + }, + resolveAuthor(users, author) { + const target = users.find(user => user.identifier === author) + const [name, hash] = this.splitHash(author) + const avatar = target ? target.avatar : this.getDefaultAvatar(author) + + return { + name, + hash, + avatar, + } + }, + resolveAuthors(users, authors) { + return authors.map(author => this.resolveAuthor(users, author)) + }, +} diff --git a/scripts/fetchUsers.js b/scripts/fetchUsers.js new file mode 100644 index 0000000..fe745e2 --- /dev/null +++ b/scripts/fetchUsers.js @@ -0,0 +1,42 @@ +// This script will be ran periodically through CI +// don't touch it +const Discord = require("discord.js") +const fs = require("fs") +const { Readable, Writable } = require("stream") + +const client = new Discord.Client() + +const { BOT_TOKEN } = process.env +const TPH = "244230771232079873" +const DESTINATION = "./users.json" + +if (!BOT_TOKEN) { + throw Error("Missing BOT_TOKEN environment variable") +} + +const writeS = (content, dest) => { + const source = new Readable() + source.push(JSON.stringify(content)) + source.push(null) + source.pipe(dest) +} + +client.once("ready", async () => { + console.log("Bot ready, fetching user list...") + const tph = client.guilds.get(TPH) + if (!tph) { + throw Error("Bot is not in TPH, cannot fetch users") + } + const guild = await tph.fetchMembers() + const memberInfo = guild.members.map(member => ({ + identifier: `${member.user.username}#${member.user.discriminator}`, + avatar: member.user.displayAvatarURL, + })) + const wrs = fs.createWriteStream(DESTINATION) + console.log(`Fetched ${memberInfo.length} users, writing to ${DESTINATION}`) + writeS(memberInfo, wrs) + process.exit(0) +}) + +console.log("Attempting to log in...") +client.login(process.env.BOT_TOKEN) diff --git a/src/components/DiscordButton/index.tsx b/src/components/DiscordButton/index.tsx new file mode 100644 index 0000000..5a748e8 --- /dev/null +++ b/src/components/DiscordButton/index.tsx @@ -0,0 +1,14 @@ +import React from "react" + +function DiscordButton({ + lined = false, + children, +}: React.PropsWithChildren<{ lined: boolean }>) { + return ( + + ) +} + +export default DiscordButton diff --git a/src/components/DocsSidebar/index.tsx b/src/components/DocsSidebar/index.tsx index 81d7478..7dcab99 100644 --- a/src/components/DocsSidebar/index.tsx +++ b/src/components/DocsSidebar/index.tsx @@ -45,7 +45,8 @@ const ALL_DOCS = graphql` relativePath childMarkdownRemark { frontmatter { - author + authors + title date } } diff --git a/src/content/docs/javascript/callbacks/intro.md b/src/content/docs/javascript/callbacks/intro.md deleted file mode 100644 index ae0a8c2..0000000 --- a/src/content/docs/javascript/callbacks/intro.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -authors: - - "@Xetera" -created_at: 2019/07/27 -title: Callbacks ---- - -# Callbacks are everywhere - -If you've been doing javascript for any amount of time you'll have noticed that callbacks appear in just about every single piece of code. diff --git a/src/content/docs/javascript/iterative-vs-functional.md b/src/content/docs/javascript/iterative-vs-functional.md index 3aa35d5..a515781 100644 --- a/src/content/docs/javascript/iterative-vs-functional.md +++ b/src/content/docs/javascript/iterative-vs-functional.md @@ -1,6 +1,6 @@ --- authors: - - "@veksen" + - "veksen#1060" created_at: "2019/07/27" --- diff --git a/src/content/docs/javascript/promises/intro.md b/src/content/docs/javascript/promises/intro.md index 64b94db..a57e13e 100644 --- a/src/content/docs/javascript/promises/intro.md +++ b/src/content/docs/javascript/promises/intro.md @@ -1,6 +1,6 @@ --- authors: - - "@Xetera" + - "Xetera#0001" created_at: 2019/07/26 updated_at: 2019/07/26 title: Introduction to Promises diff --git a/src/content/docs/javascript/promises/simplifying-promises.md b/src/content/docs/javascript/promises/simplifying-promises.md index 261d1fb..ce4eff4 100644 --- a/src/content/docs/javascript/promises/simplifying-promises.md +++ b/src/content/docs/javascript/promises/simplifying-promises.md @@ -1,6 +1,6 @@ --- authors: - - "@Xetera" + - "Xetera#0001" created_at: "2019/07/26" --- diff --git a/src/content/docs/php/design-patterns/singleton.md b/src/content/docs/php/design-patterns/singleton.md index 1295460..1ee26a0 100644 --- a/src/content/docs/php/design-patterns/singleton.md +++ b/src/content/docs/php/design-patterns/singleton.md @@ -1,6 +1,6 @@ --- authors: - - "@supergrecko" + - "supergrecko#3434" created_at: "2019/07/27" --- diff --git a/src/content/docs/python/basics.md b/src/content/docs/python/basics.md index 8f768f1..4a9fb27 100644 --- a/src/content/docs/python/basics.md +++ b/src/content/docs/python/basics.md @@ -1,5 +1,6 @@ --- -author: "@Xetera" +authors: + - "Xetera#0001" date: 2019/06/26 --- From aed86c901a0de3b977d686d1bcf395d49ecf74cc Mon Sep 17 00:00:00 2001 From: Xetera Date: Sun, 28 Jul 2019 23:10:03 -0700 Subject: [PATCH 3/8] modified language template query --- src/content/docs/javascript/callbacks/intro.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/content/docs/javascript/callbacks/intro.md diff --git a/src/content/docs/javascript/callbacks/intro.md b/src/content/docs/javascript/callbacks/intro.md new file mode 100644 index 0000000..c490815 --- /dev/null +++ b/src/content/docs/javascript/callbacks/intro.md @@ -0,0 +1,10 @@ +--- +authors: + - "Xetera#0001" +created_at: 2019/07/27 +title: Callbacks +--- + +# Callbacks are everywhere + +If you've been doing javascript for any amount of time you'll have noticed that callbacks appear in just about every single piece of code. From 71b6078e40871db73a74b749fd0394a992904f6d Mon Sep 17 00:00:00 2001 From: Xetera Date: Sun, 28 Jul 2019 23:15:21 -0700 Subject: [PATCH 4/8] added a script for fetching user information --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 075f3b2..fb8d4c7 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "start": "npm run develop", "serve": "gatsby serve", "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\"", - "codegen": "graphql-codegen --config codegen.yml" + "codegen": "graphql-codegen --config codegen.yml", + "fetch-users": "node ./scripts/fetchUsers.js" }, "repository": { "type": "git", From 2fd129acc26d552524ff797bed76739684262b8d Mon Sep 17 00:00:00 2001 From: Xetera Date: Sun, 28 Jul 2019 23:19:49 -0700 Subject: [PATCH 5/8] fixed error with buildHelper --- scripts/buildHelpers.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/buildHelpers.js b/scripts/buildHelpers.js index ccb75eb..6f86bfd 100644 --- a/scripts/buildHelpers.js +++ b/scripts/buildHelpers.js @@ -11,16 +11,14 @@ module.exports = { const hash = identifier.slice(hashLocation + 1) return [name, hash] }, - getDefaultAvatar(name) { - const [_, hash] = this.splitHash(hashLocation) - - const avatar = Number(hash) % DEFAULT_AVATAR_MODULO + getDefaultAvatar(hash) { + const avatar = hash % DEFAULT_AVATAR_MODULO return `https://cdn.discordapp.com/embed/avatars/${avatar}.png` }, resolveAuthor(users, author) { const target = users.find(user => user.identifier === author) const [name, hash] = this.splitHash(author) - const avatar = target ? target.avatar : this.getDefaultAvatar(author) + const avatar = target ? target.avatar : this.getDefaultAvatar(hash) return { name, From dadafd2670549ae5f6679e74093d260853f53a42 Mon Sep 17 00:00:00 2001 From: Xetera Date: Mon, 29 Jul 2019 00:12:35 -0700 Subject: [PATCH 6/8] update readme to match new authors format --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69057d1..c99da5f 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ An example frontmatter might look like this ```md --- authors: - - "@Xetera" + - "Xetera#0001" title: "Working with Lists in Elixir" created_at: 2020/01/30 external_resources: From 8107b6802169eb6a2e4806698123e9f1bb1b3dcf Mon Sep 17 00:00:00 2001 From: Xetera Date: Mon, 29 Jul 2019 00:13:08 -0700 Subject: [PATCH 7/8] updated language post to match changes --- src/templates/languagePost.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/templates/languagePost.tsx b/src/templates/languagePost.tsx index 4f83889..8689af2 100644 --- a/src/templates/languagePost.tsx +++ b/src/templates/languagePost.tsx @@ -22,8 +22,15 @@ export const query = graphql` file(relativePath: { eq: $file }) { post: childMarkdownRemark { html + fields { + authors { + name + hash + avatar + } + } frontmatter { - authors + date title } } From 079185e3ccd75e9b6809be1186c03b094c88318b Mon Sep 17 00:00:00 2001 From: Xetera Date: Mon, 29 Jul 2019 01:00:38 -0700 Subject: [PATCH 8/8] fixed problematic fetchusers script --- scripts/fetchUsers.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/fetchUsers.js b/scripts/fetchUsers.js index fe745e2..dd725a3 100644 --- a/scripts/fetchUsers.js +++ b/scripts/fetchUsers.js @@ -18,7 +18,7 @@ const writeS = (content, dest) => { const source = new Readable() source.push(JSON.stringify(content)) source.push(null) - source.pipe(dest) + return source.pipe(dest) } client.once("ready", async () => { @@ -34,8 +34,10 @@ client.once("ready", async () => { })) const wrs = fs.createWriteStream(DESTINATION) console.log(`Fetched ${memberInfo.length} users, writing to ${DESTINATION}`) - writeS(memberInfo, wrs) - process.exit(0) + writeS(memberInfo, wrs).on("finish", () => { + console.log("Finished writing list") + process.exit(0) + }) }) console.log("Attempting to log in...")