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
---