├── examples ├── example.js └── discord.js ├── src ├── config.json ├── Events │ ├── WatchEvent.js │ ├── ForkEvent.js │ ├── PullRequestReviewEvent.js │ ├── IssueCommentEvent.js │ ├── PushEvent.js │ ├── IssuesEvent.js │ ├── DeleteEvent.js │ ├── CreateEvent.js │ └── PullRequestEvent.js ├── util.js └── index.js ├── package.json └── README.md /examples/example.js: -------------------------------------------------------------------------------- 1 | const Github = require("gitcord") 2 | const github = new Github("CTK-WARRIOR", { 3 | repositories: ["Discord-Bot-For-Starters", "canvas-senpai"] 4 | }) 5 | github.setup() 6 | 7 | github.on('newEvent', (json) => { 8 | console.log(json) 9 | }) -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "TimeLimit": 60000, 3 | "Headers": { 4 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36", 5 | "content-type": "application/json; charset=utf-8", 6 | "accept-language": "en-US", 7 | "accept-encoding": "gzip" 8 | } 9 | } -------------------------------------------------------------------------------- /src/Events/WatchEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, {color="#161b22"}={}) => { 2 | return { 3 | color, 4 | author: { 5 | name: json.actor.login, 6 | icon_url: json.actor.avatar_url 7 | }, 8 | title: `[${json.repo.name}] New star added`, 9 | url: `https://github.com/${json.repo.name}`, 10 | footer: { text: "Github"}, 11 | timestamp: new Date() 12 | } 13 | } -------------------------------------------------------------------------------- /src/Events/ForkEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, { color = "BLUE" } = {}) => { 2 | return { 3 | color, 4 | author: { name: json.actor.login, icon_url: json.actor.avatar_url }, 5 | title: `[${json.repo.name}] Fork Created : ${json.payload.forkee.full_name}`, 6 | url: json.payload.forkee.html_url, 7 | footer: { text: "Github" }, 8 | timestamp: new Date() 9 | } 10 | } -------------------------------------------------------------------------------- /src/Events/PullRequestReviewEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, {color="#161b22"}={}) => { 2 | return { 3 | color, 4 | author: { 5 | name: json.actor.login, 6 | icon_url: json.actor.avatar_url 7 | }, 8 | title: `[${json.repo.name}] Pull request review submitted: #${json.payload.pull_request.number} ${json.payload.pull_request.title}`, 9 | url: json.payload.review.html_url, 10 | description: json.payload.review.body 11 | } 12 | } -------------------------------------------------------------------------------- /examples/discord.js: -------------------------------------------------------------------------------- 1 | const Github = require("gitcord") 2 | const Discord = require("discord.js") 3 | const client = new Discord.Client(); 4 | const github = new Github("CTK-WARRIOR", { repositories: ["GitCord"] }) 5 | github.setup() 6 | 7 | client.on("ready", () => { 8 | console.log("Connected to the discord, now ready for fight :D") 9 | }) 10 | 11 | github.on("newEvent", (embed) => { 12 | client.channels.cache.get("CHANNEL ID").send({embed}) 13 | }) 14 | 15 | client.login("TOKEN") -------------------------------------------------------------------------------- /src/Events/IssueCommentEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, {color="#e68d60"}={}) => { 2 | return { 3 | color: json.payload.issue.pull_request ? "#bfe5bf" : "#e68d60", 4 | author: { name: json.actor.login, icon_url: json.actor.avatar_url }, 5 | title: `[${json.repo.name}] New comment on ${json.payload.issue.pull_request ? 'pull request' : 'issue'} #${json.payload.issue.number}: ${json.payload.issue.title}`, 6 | url: json.payload.comment.html_url, 7 | description: json.payload.comment.body 8 | } 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Events/PushEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, { color="blue" } = {}) => { 2 | return { 3 | color, 4 | author: { 5 | name: json.actor.login, 6 | icon_url: json.actor.avatar_url 7 | }, 8 | title: `[${json.repo.name}] ${json.payload.commits.length} New commit`, 9 | url: `https://github.com/${json.repo.name}/commit/${json.payload.head}`, 10 | description: json.payload.commits.map(x => `[\`${x.sha.substring(0, 7)}\`](https://github.com/${json.repo.name}/commit/${x.sha}) ${x.message} - ${x.author.name}`).join("\n"), 11 | footer: { text: "Github" }, 12 | timestamp: new Date() 13 | }; 14 | }; -------------------------------------------------------------------------------- /src/Events/IssuesEvent.js: -------------------------------------------------------------------------------- 1 | const colorObject = { closed: "RED", opened: "GREEN", reopened: "#161b22" } 2 | 3 | module.exports = (json, { color } = {}) => { 4 | return { 5 | color: color ? color : (colorObject[json.payload.action] ? colorObject[json.payload.action] : "GREEN"), 6 | author: { name: json.actor.login, icon_url: json.actor.avatar_url }, 7 | title: `[${json.repo.name}] Issue ${json.payload.action}: #${json.payload.issue.number} ${json.payload.issue.title}`, 8 | url: json.payload.issue.html_url, 9 | description: json.payload.action === "opened" ? json.payload.issue.body : null 10 | } 11 | } -------------------------------------------------------------------------------- /src/Events/DeleteEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, { color = "RED" } = {}) => { 2 | const obj = { 3 | color, 4 | author: { name: json.actor.login, icon_url: json.actor.avatar_url }, 5 | title: ["branch", "tag"].includes(json.payload.ref_type) ? `[${json.repo.name}] ${json.payload.ref_type} deleted: ${json.payload.ref}` : json.payload.ref_type === "repository" ? `[${json.repo.name}] repository deleted` : `[${json.repo.name}] something deleted`, 6 | url: `https://github.com/${json.repo.name}`, 7 | description: json.payload.description, 8 | footer: { text: "Github" }, 9 | timestamp: new Date() 10 | }; 11 | 12 | 13 | return obj; 14 | }; -------------------------------------------------------------------------------- /src/Events/CreateEvent.js: -------------------------------------------------------------------------------- 1 | module.exports = (json, { color="GREEN" } = {}) => { 2 | const obj = { 3 | color, 4 | author: { name: json.actor.login, icon_url: json.actor.avatar_url }, 5 | title: ["branch", "tag"].includes(json.payload.ref_type) ? `[${json.repo.name}] new ${json.payload.ref_type} created: ${json.payload.ref}` : json.payload.ref_type === "repository" ? `[${json.repo.name}] new repository created` : `[${json.repo.name}] something created`, 6 | url: `https://github.com/${json.repo.name}`, 7 | description: json.payload.description, 8 | footer: { text: "Github" }, 9 | timestamp: new Date() 10 | }; 11 | 12 | return obj; 13 | }; -------------------------------------------------------------------------------- /src/Events/PullRequestEvent.js: -------------------------------------------------------------------------------- 1 | const colorObject = { closed: "RED", opened: "GREEN", reopened: "#161b22" } 2 | 3 | module.exports = (json, { color } = {}) => { 4 | return { 5 | color: color ? color : (colorObject[json.payload.action] ? colorObject[json.payload.action] : "GREEN"), 6 | author: { name: json.actor.login, icon_url: json.actor.avatar_url }, 7 | title: `[${json.repo.name}] Pull request ${json.payload.action} #${json.payload.pull_request.number}: ${json.payload.pull_request.title}`, 8 | url: json.payload.pull_request.html_url, 9 | description: json.payload.action.includes("opened") ? json.payload.pull_request.body : null 10 | } 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitcord", 3 | "version": "1.0.3", 4 | "description": "GitCord allows you to send the github feeds at your discord server without having any issue.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/CTK-WARRIOR/GitCord.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/CTK-WARRIOR/GitCord/issues" 15 | }, 16 | "keywords": [ 17 | "Github", 18 | "github-notifier", 19 | "githubfeed", 20 | "GitCord", 21 | "discord-github" 22 | ], 23 | "author": "ctk", 24 | "license": "ISC", 25 | "dependencies": { 26 | "axios": "^0.21.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitCord 2 |


3 |

4 | Discord 5 |

6 | 7 | > GitCord allows you to send the github feeds to your discord server without having any issue. 8 | 9 | ## LINKS 10 | 11 | - 📃 Guide/Docs: `Soon` 12 | - 💬 Discord: https://withwin.in/dbd 13 | - 🎥 Youtube: https://www.youtube.com/channel/UClAFgotVhZ1DGvN57EMY7fA 14 | - 🙌 Video Tutorial: `Not Out` 15 | - 💪 Special Contributor: [Legendary Emoji#1742](https://github.com/LegendaryEmoji) 16 | - 🛠 Tools Used: [Axios](https://www.npmjs.com/package/axios) 17 | 18 | ## Features 19 | 20 | - Allows you to easily get the event details 21 | - Fast and Highly Configurable 22 | - Easy to Implement 23 | 24 | ## Example 25 | 26 | ```js 27 | const Github = require("gitcord") 28 | const github = new Github("CTK-WARRIOR", { 29 | repositories: ["Discord-Bot-For-Starters", "canvas-senpai"] 30 | }) 31 | github.setup() 32 | 33 | github.on('newEvent', (json) => { 34 | //do something 35 | }) 36 | ``` 37 | ## Without Manual Repositories Addition 38 | Basically you have to add the array of repositories name in options or subscribe repository in order to get feeds related to that repository but we have implemented very usefull feauture for you to get rid of these long chain process. 39 | 40 | ```js 41 | const Github = require("gitcord") 42 | const github = new Github("CTK-WARRIOR", { token: "Your Super Cool Github Token", gitall: true }) //will throw error if user have more than 50 repo 43 | github.setup() 44 | 45 | github.on('newEvent', (json) => { 46 | //do something 47 | }) 48 | ``` 49 | 50 | ## 🐱‍🏍 How to use on Discord ? 51 | ```js 52 | const Github = require("gitcord") 53 | const Discord = require("discord.js") 54 | const client = new Discord.Client(); 55 | const github = new Github("CTK-WARRIOR", { token: "Your Super Cool Github Token", gitall: true }) 56 | github.setup() 57 | 58 | client.on("ready", () => { 59 | console.log("Connected to the discord, now ready for fight :D") 60 | }) 61 | 62 | github.on("newEvent", (embed) => { 63 | client.channels.cache.get("CHANNEL ID").send({embed}) 64 | }) 65 | 66 | client.login("YOUR DISCORD TOKEN") 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * I like Axios more than Fetch :D 3 | */ 4 | const Axios = require("axios").default; 5 | 6 | /** 7 | * Function to handle the errors 8 | * @param {string} [msg] 9 | */ 10 | async function __err(msg) { 11 | throw new Error(msg); 12 | }; 13 | 14 | /** 15 | * Function to get the json data from github API 16 | * @param {string} [username] 17 | * @param {Object} [headers] 18 | * @param {Array} [repository] 19 | * @return {Object} JSON data fetched from API 20 | */ 21 | async function fetchUserData(username, headers, repository, func = (response) => { return response }) { 22 | if (!username) return __err("Missing \"username\" Argument"); 23 | const { data } = await Axios.get(`https://api.github.com/repos/${encodeURI(username)}/${encodeURI(repository)}/events`, { responseType: "json", headers }) 24 | 25 | return func(data); 26 | }; 27 | 28 | /** 29 | * Function to check if there is any new event 30 | * @param {Object} [options] 31 | * @property {string} [options.username] 32 | * @property {Object} [options.events] 33 | * @property {Object} [options.headers] 34 | * @property {string} [options.color] 35 | * @property {Array} [options.repository] 36 | * @property {Object} [options.github] 37 | */ 38 | async function checkWhatsNew({ username, events, headers, color, repository, github } = {}) { 39 | 40 | for (let repo of repository) { 41 | const data = await fetchUserData(username, headers, repo); 42 | const latestEvents = data.filter((element) => !events[repo].includes(element.id)); 43 | if (!latestEvents.length) continue; 44 | 45 | for (let Element of latestEvents.reverse()) { 46 | try { 47 | github.emit("newEvent", require(`./Events/${Element.type}`)(Element, { color })) 48 | } catch (err) { 49 | if (err.toString().includes("Cannot find module")) console.log({ event: Element.type, message: "this event is ignored because its not supported for now, let us know if you want this event to be added." }) 50 | } 51 | events[repo].push(Element.id); 52 | }; 53 | } 54 | }; 55 | 56 | /** 57 | * Function to get all the repository of provided Github user 58 | * @param {string} [username] 59 | * @param {Object} [headers] 60 | * @return {Array} Array of all repositories name 61 | */ 62 | async function gitAll(username, headers) { 63 | if(!headers.Authorization) __err("Github token is required for this action") 64 | const { data } = await Axios.get(`https://api.github.com/users/${encodeURI(username)}/repos`, { responseType: "json", headers }) 65 | if(data.size > 50) __err("Your github profile have more than 50 repositories which is why you can not use gitAll Feeds feauture.") 66 | 67 | return data.map(x => x.name) 68 | } 69 | 70 | module.exports = { fetchUserData, checkWhatsNew, gitAll }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Modules and Variables 3 | */ 4 | const { fetchUserData, checkWhatsNew, gitAll } = require("./util.js"); 5 | const EventEmitter = require('events'); 6 | const { TimeLimit, Headers } = require("./config.json"); 7 | 8 | /** 9 | * Github classs 10 | * extended with EventEmitter 11 | */ 12 | 13 | class Github extends EventEmitter { 14 | /** 15 | * @typedef {Object} Options 16 | * @property {string} [options.color] 17 | * @property {string} [options.token] github token which is required to increase the rate limit to 5000 18 | * @property {number} [options.intervalTime] time of the interval of checking the github events 19 | */ 20 | 21 | /** 22 | * @constructor 23 | * @param {string} [username] github username of the user 24 | * @param {Options} [color, token, intervalTime] options for the github feeds 25 | */ 26 | constructor(username, { color, token, intervalTime, repositories, gitall } = {}) { 27 | if (!username) throw new Error('You need to provode your github username.') 28 | if (token) Headers.Authorization = `token ${token}` 29 | super() 30 | 31 | /** 32 | * Github display username of the user. 33 | * @type {string} 34 | */ 35 | this.username = username 36 | 37 | /** 38 | * Hex color for the json embed (optional) 39 | * @type {string} 40 | */ 41 | this.color = color 42 | 43 | /** 44 | * Token of the github user, token will increase the rate limit to 5000/h (using token is highly suggested) 45 | * @type {string} 46 | */ 47 | this.token = token 48 | 49 | /** 50 | * Time for the interval for checking the API 51 | * @type {number} 52 | */ 53 | this.intervalTime = intervalTime || TimeLimit 54 | 55 | /** 56 | * List of repositories that we will watch for events 57 | * @type {Array} 58 | */ 59 | this.repositories = repositories || [] 60 | 61 | /** 62 | * List of the all the stored events which are being compare with new one for feeds 63 | */ 64 | this.events = {} 65 | 66 | /** 67 | * If this enable then it ignores the [repositories] option and get all repositories of user from its profile but it do have limit of 50 repositories, so the error will be thrown if repositories number reach to 50 or more. 68 | */ 69 | this.gitall = gitall || false 70 | } 71 | 72 | /** 73 | * Start to watch all events 74 | */ 75 | async setup() { 76 | if(this.gitall) this.repositories = await gitAll(this.username, Headers) 77 | 78 | for (let repo of this.repositories) fetchUserData(this.username, Headers, repo, (eventsObject) => (this.events[repo] = eventsObject.map((e) => e.id))) 79 | 80 | setInterval(() => this.repositories.length ? checkWhatsNew({ username: this.username, events: this.events, headers: Headers, color: this.color, repository: this.repositories, github: this }) : "", this.intervalTime) 81 | } 82 | 83 | /** 84 | * subscribe to github repository so that you can receive feeds related to it 85 | * @param {string|Array} github repository name 86 | */ 87 | subscribe(repository) { 88 | const RepoArray = typeof repository === 'string' ? [repository] : repository 89 | 90 | for (let repo of RepoArray) { 91 | fetchUserData(this.username, Headers, repo, (eventsObject) => (this.events[repo] = eventsObject.map((e) => e.id))) 92 | !this.repositories.includes(repo) ? this.repositories.push(repo) : "" 93 | } 94 | } 95 | 96 | /** 97 | * unsubsribe to github repository to stop reciving calls from event 98 | * @param {string|Array} github repository name 99 | */ 100 | 101 | unsubscribe(repository) { 102 | let repoArray = typeof repository === 'string' ? [repository] : repository 103 | repoArray = repoArray.filter(x => this.repositories.includes(x)) 104 | 105 | for (let repo of repoArray) { 106 | delete this.events[repo] 107 | this.repositories.splice(this.repositories.indexOf(repo), 1) 108 | } 109 | } 110 | 111 | } 112 | 113 | module.exports = Github --------------------------------------------------------------------------------