├── Procfile
├── .babelrc
├── .DS_Store
├── CHANGELOG
├── src
├── events
│ ├── ready.js
│ ├── message.js
│ └── schedule.js
├── config
│ ├── .env.example
│ └── index.js
├── models
│ ├── Member.js
│ └── Server.js
├── utils
│ ├── utils.js
│ └── mongoose.js
├── commands
│ ├── help.js
│ ├── birthdays.js
│ ├── info
│ │ └── commands.js
│ ├── whatsMyBirthday.js
│ ├── nextBirthday.js
│ └── birthday.js
├── repositories
│ ├── server-repository.js
│ └── member-repository.js
├── handlers
│ └── command.js
└── index.js
├── .gitignore
├── .github
├── workflows
│ └── main.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── LICENSE
├── CONTRIBUTING.es.md
├── package.json
├── CONTRIBUTING.md
└── README.md
/Procfile:
--------------------------------------------------------------------------------
1 | worker: node src/index.js
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tomassirio/BirthdayBot/HEAD/.DS_Store
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | 1.0.0 - First version. Birthday command created. Doesn't connect to the DB yet
--------------------------------------------------------------------------------
/src/events/ready.js:
--------------------------------------------------------------------------------
1 | module.exports = async (client) => {
2 | await client.mongoose.init()
3 |
4 | console.log(`Logged in as ${client.user.tag}!`)
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project entries
2 | src/config/.env
3 | build/config/.env
4 |
5 | build
6 | node_modules
7 |
8 | # Database entries
9 | data
10 |
11 | # IDE entries
12 | .idea
13 |
--------------------------------------------------------------------------------
/src/config/.env.example:
--------------------------------------------------------------------------------
1 | PREFIX=$
2 |
3 | BOT_TOKEN= {DISCORD BOT TOKEN}
4 | LOCAL_DB_USER= {MONGODB USERNAME}
5 | LOCAL_DB_PASSWORD= {MONGODB PASSWORD}
6 | LOCAL_DB_MONGO= {MONGODB DEFAUTL DB}
7 | DB_USER= {MONGODB USERNAME}
8 | DB_PASSWORD= {MONGODB PASSWORD}
9 | DB_MONGO= {MONGODB DEFAUTL DB}
10 |
--------------------------------------------------------------------------------
/src/models/Member.js:
--------------------------------------------------------------------------------
1 | const { Schema, model } = require('mongoose')
2 |
3 | const memberSchema = Schema({
4 | birthday: {
5 | default: '',
6 | type: Date,
7 | },
8 | user: {
9 | default: '',
10 | type: String,
11 | },
12 | })
13 |
14 | module.exports = model('Member', memberSchema, 'members')
15 |
--------------------------------------------------------------------------------
/src/models/Server.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const serverSchema = mongoose.Schema({
4 | serverId: {
5 | default: '1',
6 | type: String,
7 | },
8 | name: {
9 | default: 'emptyServer',
10 | type: String,
11 | },
12 | members: [],
13 | })
14 |
15 | module.exports = mongoose.model('Server', serverSchema, 'servers')
16 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 |
3 | jobs:
4 | contrib-readme-job:
5 | runs-on: ubuntu-latest
6 | name: A job to automate contrib in readme
7 | steps:
8 | - name: Contribute List
9 | uses: akhilmhdh/contributors-readme-action@v2.0.1
10 | env:
11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
12 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-unresolved
2 | const Discord = require('discord.js')
3 |
4 | const Utils = {
5 | embedMessage(message, author, color, item) {
6 | const embed = new Discord.MessageEmbed()
7 | .setTitle(message)
8 | .setColor(color)
9 | .setDescription(item)
10 | .setFooter(author)
11 | .setTimestamp()
12 | return embed
13 | },
14 | }
15 |
16 | module.exports = Utils
17 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: '[BUG]'
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 |
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: '[FEATURE]'
5 | labels: Feature
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 | const path = require('path')
3 | const result = require('dotenv').config({ path: path.join(__dirname, '.env') })
4 |
5 | if (result.error) throw result.error // Throw an error if it failed to load the content off the .env file.
6 |
7 | module.exports = {
8 | prefix: process.env.PREFIX,
9 | botToken: process.env.BOT_TOKEN,
10 | devMongoUrl: "mongodb+srv://".concat(process.env.LOCAL_DB_USER, ":").concat(process.env.LOCAL_DB_PASS,"@cluster0.mhoa7.mongodb.net/").concat(process.env.LOCAL_DB_MONGO, "?retryWrites=true&w=majority"),
11 | productionMongoURL: "mongodb+srv://".concat(process.env.DB_USER, ":").concat(process.env.DB_PASSWORD, "@cluster0.mhoa7.mongodb.net/").concat(process.env.DB_MONGO, "?retryWrites=true&w=majority"),
12 | }
13 |
--------------------------------------------------------------------------------
/src/events/message.js:
--------------------------------------------------------------------------------
1 | const { prefix } = require('../config')
2 |
3 | module.exports = async (client, message) => {
4 |
5 | let flag = false;
6 |
7 | message.guild.channels.cache.map((channel) => {
8 | if(channel.name == "wishes")
9 | flag = true;
10 | });
11 |
12 | if(!flag)
13 | message.guild.channels.create("wishes", "text/voice");
14 |
15 | if (!message.content.startsWith(prefix) || message.author.bot) return
16 |
17 | let args = message.content
18 | .slice(prefix.length)
19 | .trim()
20 | .split(/ +/)
21 | const command = args.shift().toLowerCase()
22 |
23 | if (!client.commands.has(command)) return
24 |
25 |
26 | try {
27 | client.commands.get(command).execute(message, args)
28 | } catch (error) {
29 | console.error(error)
30 | message.reply('There was an error trying to execute that command!')
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/commands/help.js:
--------------------------------------------------------------------------------
1 | const Util = require('../utils/utils.js')
2 |
3 | module.exports = {
4 | name: 'help',
5 | description: 'Provides help about the commands',
6 | execute: async (message) => {
7 | const { channel } = message
8 | const color = '0xffff00'
9 |
10 | let embededMessage;
11 | embededMessage = Util.embedMessage(
12 | 'Help',
13 | message.author.tag,
14 | color,
15 | process.env.PREFIX + 'birthday
: adds your birthday to the server\n\n' +
16 | process.env.PREFIX + 'birthdays: brings all the birthdays in the server\n\n' +
17 | process.env.PREFIX + 'nextBirthday: gets the next server\'s birthday\n\n' +
18 | process.env.PREFIX + 'whatsMyBirthday: gets your birthday\n\n'+
19 | process.env.PREFIX + 'help: shows all commands and their description\n'
20 | );
21 | channel.send(embededMessage)
22 | },
23 | }
--------------------------------------------------------------------------------
/src/repositories/server-repository.js:
--------------------------------------------------------------------------------
1 | const Server = require('../models/Server')
2 |
3 | const ServerRepository = {
4 | find: async (id) => {
5 | try {
6 | return await Server.findOne({
7 | serverId: id,
8 | })
9 | } catch (e) {
10 | console.log(e)
11 | return null
12 | }
13 | },
14 |
15 | create: async (serverData) => {
16 | const server = new Server({
17 | serverId: serverData.id,
18 | name: serverData.name,
19 | members: [],
20 | })
21 |
22 | try {
23 | server.save()
24 | console.log('Stored', server)
25 | } catch (e) {
26 | console.error(e)
27 | }
28 |
29 | return server
30 | },
31 |
32 | findOrCreate: async (serverData) => {
33 | const server = await ServerRepository.find(serverData.id)
34 |
35 | if (server) {
36 | return server
37 | }
38 |
39 | return ServerRepository.create(serverData)
40 | },
41 | }
42 |
43 | module.exports = ServerRepository
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Tomas Sirio
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 |
--------------------------------------------------------------------------------
/src/utils/mongoose.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 | const mongoose = require('mongoose')
3 | const { devMongoUrl, productionMongoURL } = require('../config')
4 |
5 | mongoose.set('useFindAndModify', false)
6 | mongoose.Promise = global.Promise
7 |
8 | mongoose.connection.on('connected', () => {
9 | console.log('Mongoose has suceesfully connected.')
10 | })
11 |
12 | mongoose.connection.on('err', () => {
13 | console.error(`Mongoose connection error: \n${err.stack}.`)
14 | })
15 |
16 | mongoose.connection.on('disconected', () => {
17 | console.warn('Mongoose has disconnected.')
18 | })
19 |
20 | module.exports = {
21 | init: async () => {
22 | const dbOptions = {
23 | useNewUrlParser: true,
24 | useUnifiedTopology: true,
25 | autoIndex: false,
26 | poolSize: 5,
27 | connectTimeoutMS: 10000,
28 | family: 4,
29 | }
30 |
31 | if (process.env.NODE_ENV === 'development') {
32 | await mongoose.connect(devMongoUrl, dbOptions)
33 | } else {
34 | await mongoose.connect(productionMongoURL, dbOptions)
35 | }
36 |
37 | return mongoose.connection
38 | },
39 | }
40 |
--------------------------------------------------------------------------------
/src/handlers/command.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable new-cap */
2 | /* eslint-disable import/no-unresolved */
3 | /* eslint-disable global-require */
4 | /* eslint-disable no-restricted-syntax */
5 | const { readdirSync } = require('fs')
6 | const ascii = require('ascii-table')
7 |
8 | const table = new ascii().setHeading('Command', 'Status')
9 | const path = require('path')
10 |
11 | module.exports = (client) => {
12 | const fullPath = path.resolve(__dirname, `../commands/`)
13 | const commands = readdirSync(fullPath).filter((f) => f.endsWith('.js'))
14 | for (const file of commands) {
15 | // eslint-disable-next-line import/no-dynamic-require
16 | const pull = require(`../commands/${file}`)
17 |
18 | if (pull.name) {
19 | client.commands.set(pull.name, pull)
20 | table.addRow(file, '✅ Loaded!')
21 | } else {
22 | table.addRow(
23 | file,
24 | '❌ -> Command failed to load, please check your work again!'
25 | )
26 | }
27 |
28 | if (pull.aliases && Array.isArray(pull.aliases))
29 | pull.aliases.forEach((alias) => {
30 | return client.aliases.set(alias, pull.name)
31 | })
32 | }
33 |
34 | console.log(table.toString())
35 | }
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | require('@babel/polyfill')
2 | const { Client, Collection } = require('discord.js')
3 | const { botToken } = require('./config')
4 | const fs = require('fs')
5 | const path = require('path')
6 | const client = new Client()
7 |
8 | client.commands = new Collection()
9 | client.aliases = new Collection()
10 | client.mongoose = require('./utils/mongoose')
11 |
12 | client.categories = fs.readdirSync(path.resolve(__dirname, './commands/'))
13 | ;['command'].forEach((handler) => {
14 | require(`./handlers/${handler}`)(client)
15 | })
16 |
17 | const files = fs.readdirSync(path.join(__dirname, '/events')) // Read the content files in the directory before starting the bot.
18 | files.forEach((fileName) => {
19 | if (fileName.endsWith('.js')) {
20 | // Looking for .js files only.
21 | const event = require(`./events/${fileName}`)
22 | const eventName = fileName.split('.')[0] // Get the event name of the file.
23 |
24 | console.log(`Successfully loaded the ${eventName} event.`)
25 | client.on(eventName, event.bind(null, client))
26 |
27 | client.on('ready', () => {
28 | if (eventName === 'schedule') {
29 | event(client) // trigger schedule event
30 | }
31 | })
32 | }
33 | })
34 |
35 | client.login(botToken)
36 |
--------------------------------------------------------------------------------
/CONTRIBUTING.es.md:
--------------------------------------------------------------------------------
1 | ## Pasos simples para empezar:
2 |
3 | - **Si eres nuevo en Git / Github, no tienes por que sentirte intimidado**
4 | - Puedes simplemente contribuir usando la UI de Github.
5 |
6 | 1. Crea un nuevo Fork de este repositorio.
7 | 2. Has el cambio que desees.
8 | 3. Luego, crea un Pull Request.
9 | 4. Tu Pull Request sera evaluado y finalmente mergeado al master (main).
10 |
11 | [Tambien puedes guiarte en este tutorial](https://github.com/firstcontributions/first-contributions)
12 |
13 | ## Escribiendo codigo:
14 |
15 | - Antes de hacer `push` de tus cambios a Github, asegurate de que tu código compile y corra sin errores ni warnings
16 | - Aceptamos contribuciones en cualquier lenguaje (Inglés/Español)
17 | - Usa variables, métodos y nombres de funciones declarativas junto a los comentarios
18 | - Nada de profanidades
19 |
20 | ## Maneras de contribuir:
21 |
22 | - Implementa nuevos algoritmos para el repositorio en su debida sección. Si la seccion no existe, crea una apropiada
23 | - Optimiza o mejora el código existente de algún algoritmo
24 | - Agrega una nueva solución a un problema existente
25 | - Encontrar y arreglar bugs
26 | NOTA: Si ya existe un issue levantado con respecto al pull request que has hecho asegúrate de marcar con hashtag (#) el número de issue (i.e. #1)
27 |
--------------------------------------------------------------------------------
/src/repositories/member-repository.js:
--------------------------------------------------------------------------------
1 | const Member = require('../models/Member')
2 | const Server = require('../models/Server')
3 |
4 | const MemberRepository = {
5 | find: async (memberId, serverId) => {
6 | try {
7 | return await Model.findOne({server: serverId, members: { $elemMatch: { user: memberId } } })
8 | } catch (e) {
9 | console.log(e)
10 | return null
11 | }
12 | },
13 |
14 | create: async (memberData, serverData) => {
15 | const server = await Server.findOne({serverId: serverData.id})
16 | const member = new Member({
17 | birthday: memberData.birthday,
18 | user: memberData.name,
19 | })
20 |
21 | server.members.push(member)
22 |
23 | try {
24 | server.save()
25 | console.log('Stored', server)
26 | } catch (e) {
27 | console.error(e)
28 | }
29 |
30 | return member
31 | },
32 |
33 | findOrCreate: async (memberData, serverData) => {
34 | const member = await MemberRepository.find(memberData.id, serverData.id)
35 |
36 | if (member) {
37 | return member
38 | }
39 |
40 | return MemberRepository.create(memberData, serverData)
41 | },
42 | }
43 |
44 | module.exports = MemberRepository
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "BirthdayBot",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "babel src -d build && cross-env NODE_ENV=production node build/index.js",
8 | "dev": "cross-env NODE_ENV=development nodemon --exec babel-node src/index.js",
9 | "prettier": "prettier --write ."
10 | },
11 | "license": "ISC",
12 | "dependencies": {
13 | "@babel/polyfill": "^7.11.5",
14 | "ascii-table": "0.0.9",
15 | "cheerio": "^1.0.0-rc.3",
16 | "cross-env": "^7.0.2",
17 | "discord.js": "^12.3.1",
18 | "dotenv": "^8.2.0",
19 | "moment": "^2.29.1",
20 | "mongoose": "^5.10.7",
21 | "node-cron": "^2.0.3",
22 | "request": "^2.88.2",
23 | "request-promise": "^4.2.6"
24 | },
25 | "devDependencies": {
26 | "@babel/cli": "^7.11.6",
27 | "@babel/core": "^7.11.6",
28 | "@babel/node": "^7.10.5",
29 | "@babel/preset-env": "^7.11.5",
30 | "eslint": "^7.10.0",
31 | "eslint-config-airbnb-base": "^14.2.0",
32 | "eslint-config-prettier": "^6.12.0",
33 | "eslint-plugin-import": "^2.22.1",
34 | "eslint-plugin-prettier": "^3.1.4",
35 | "nodemon": "^2.0.4",
36 | "prettier": "^2.1.2"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/commands/birthdays.js:
--------------------------------------------------------------------------------
1 | const Util = require("../utils/utils.js");
2 | const ServerRepository = require("../repositories/server-repository");
3 | const moment = require("moment");
4 |
5 | module.exports = {
6 | name: "birthdays",
7 | description: "Lists all users birthdays",
8 | execute: async (message, args) => {
9 | let channel = message.channel;
10 |
11 | const dbServer = await ServerRepository.findOrCreate(message.guild);
12 |
13 | let birthdays = dbServer.members
14 | .filter((member) => member.birthday != undefined)
15 | .sort((a, b) => yearless(a.birthday) - yearless(b.birthday))
16 | .reduce(formatUsers, "");
17 |
18 | if (!birthdays) {
19 | let embededMessage = Util.embedMessage(
20 | `No Birthdays Found`,
21 | message.author.tag,
22 | "0xffff00",
23 | `Users in this server have not set their Birthdays yet!`
24 | );
25 |
26 | channel.send(embededMessage);
27 | return;
28 | }
29 |
30 | let embededMessage = Util.embedMessage(
31 | `List of All Registered Birthdays`,
32 | message.author.tag,
33 | "0xffff00",
34 | birthdays
35 | );
36 |
37 | channel.send(embededMessage);
38 | },
39 | };
40 |
41 | const formatUsers = (accumulator, member) => {
42 | const date = moment(member.birthday).format("MMM Do");
43 | return accumulator + `${date} => ${member.user}\n`;
44 | };
45 |
46 | const yearless = (aDate) => new Date(1900, aDate.getMonth(), aDate.getDay());
--------------------------------------------------------------------------------
/src/commands/info/commands.js:
--------------------------------------------------------------------------------
1 | const { MessageEmbed } = require('discord.js')
2 | const { stripIndents } = require('common-tags')
3 | const { prefix } = require('../config')
4 | module.exports = {
5 | name: 'commands',
6 | aliases: ['c'],
7 | category: 'info',
8 | description: 'Displays a full list of bot commands.',
9 | usage: `commands`,
10 | run: async (client, message) => {
11 | return getAll(client, message)
12 | },
13 | }
14 |
15 | function getAll(client, message) {
16 | const embed = new MessageEmbed()
17 | .setColor(process.env.COLOR)
18 | .setTitle('Command List')
19 | .setThumbnail(client.user.avatarURL())
20 | .setFooter('Created by Tomas Sirio')
21 |
22 | const commands = (category) => {
23 | return client.commands
24 | .filter((cmd) => cmd.category === category)
25 | .map((cmd) => `- \`${prefix + cmd.name}\``)
26 | .join('\n')
27 | }
28 |
29 | const info = client.categories
30 | .map(
31 | (cat) =>
32 | stripIndents`**${
33 | cat[0].toLowerCase() + cat.slice(1)
34 | }** \n${commands(cat)}`
35 | )
36 | .reduce((string, category) => `${string}\\n${category}`)
37 |
38 | return message.channel.send(
39 | embed.setDescription(
40 | 'Use `' +
41 | `${prefix}help \` without the \`<>\` to see more information about a specific command.\n\n${info}`
42 | )
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/src/events/schedule.js:
--------------------------------------------------------------------------------
1 | var cron = require('node-cron');
2 | const ServerRepository = require("../repositories/server-repository.js");
3 |
4 | module.exports = async (client) => {
5 | await client.mongoose.init()
6 | cron.schedule('0 9 * * *', () => {
7 | // get the current date so we can compare dates later when verifying birthdays
8 | const date = new Date();
9 | const currentMonth = date.getMonth() + 1;
10 | const currentDay = date.getDate();
11 |
12 | // get the servers
13 | client.guilds.cache.forEach(async (guild) => {
14 | const dbServer = await ServerRepository.findOrCreate(guild);
15 | let flag = false;
16 | let birthdayChannel = null;
17 | guild.channels.cache.forEach(channel => {
18 | if(channel.name == 'birthdaybot') {
19 | flag = true;
20 | birthdayChannel = channel
21 | }
22 | })
23 | // create the birthdayBot channel if the server doesnt have it
24 | if(!flag) {
25 | birthdayChannel = await guild.channels.create("birthdaybot", "text/voice");
26 | }
27 |
28 | // wish each member in the db a happy birthday
29 | dbServer.members.forEach(member => {
30 | const month = member.birthday.getUTCMonth()+1;
31 | const day = member.birthday.getUTCDate();
32 | if (month === currentMonth && day === currentDay) {
33 | birthdayChannel.send(`Today is <@${member.discord_id}>'s birthday! Happy birthday!!`)
34 | }
35 | })
36 |
37 | })
38 | }, {
39 | scheduled: true,
40 | timezone: "America/Sao_Paulo"
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/src/commands/whatsMyBirthday.js:
--------------------------------------------------------------------------------
1 | const Util = require('../utils/utils.js')
2 | const Item = require('../models/Member.js')
3 | const ServerRepository = require('../repositories/server-repository')
4 | const { db } = require('../models/Member.js')
5 | const Server = require('../models/Server.js')
6 | const MemberRepository = require('../repositories/member-repository.js')
7 | const {prefix} = require('../config')
8 |
9 | module.exports = {
10 | name: 'whatsmybirthday',
11 | description: 'Shows you your birthday',
12 | execute: async (message, _) => {
13 |
14 | let channel = message.channel
15 |
16 |
17 | const dbServer = await ServerRepository.findOrCreate(message.guild)
18 |
19 | let member = dbServer.members.find(m => m.user === message.author.tag)
20 | let embededMessage;
21 |
22 | if(member){
23 | // Dates will all come back with the same character count so slicing up to the end of the year seems simplest
24 | const formattedDate = member.birthday.toString().slice(0,15);
25 | const randomColor = Math.floor(Math.random()*16777215).toString(16);
26 |
27 | embededMessage = Util.embedMessage(
28 | 'Your Birthday',
29 | message.author.tag,
30 | `0x${randomColor}`,
31 | `Your Birthday is set to ${formattedDate}.`
32 | )
33 | }else{
34 | embededMessage = Util.embedMessage(
35 | 'Birthday not Set',
36 | message.author.tag,
37 | '0xffff00',
38 | `You have not set your Birthday! Please set your Birthday using the {${prefix}birthday dd mm yy} command.`
39 |
40 | )
41 | }
42 |
43 | channel.send(embededMessage)
44 | return
45 | },
46 | }
47 |
--------------------------------------------------------------------------------
/src/commands/nextBirthday.js:
--------------------------------------------------------------------------------
1 | const Util = require('../utils/utils.js')
2 | const ServerRepository = require('../repositories/server-repository')
3 | const moment = require("moment");
4 |
5 | module.exports = {
6 | name: 'nextbirthday',
7 | description: 'Add an element to the list',
8 | execute: async (message, args) => {
9 | let channel = message.channel;
10 |
11 | const dbServer = await ServerRepository.findOrCreate(message.guild);
12 |
13 | let birthdays = dbServer.members
14 | .filter((member) => member.birthday != undefined)
15 | .sort((a, b) => formatDate(a.birthday) - formatDate(b.birthday))
16 |
17 | let offsets = [...birthdays]
18 |
19 | if (!birthdays) {
20 | let embededMessage = Util.embedMessage(
21 | `No Birthdays Found`,
22 | message.author.tag,
23 | "0xffff00",
24 | `Users in this server have not set their Birthdays yet!`
25 | );
26 |
27 | channel.send(embededMessage);
28 | return;
29 | }
30 |
31 | let birthdayEntry = find(offsets)
32 | let birthday = formatUser(birthdays[birthdayEntry])
33 | let embededMessage = Util.embedMessage(
34 | `Next Birthday`,
35 | message.author.tag,
36 | "0xffff00",
37 | birthday
38 | );
39 |
40 | channel.send(embededMessage);
41 |
42 | },
43 | };
44 |
45 | const formatUser = (member) => {
46 | const date = moment(member.birthday).format("MMM Do");
47 | return `${date} => ${member.user}\n`;
48 | };
49 |
50 |
51 | const formatDate = (member) => {
52 | const date = moment(member.birthday).format("MM DD");
53 | return date
54 | };
55 |
56 | let find = (birthdays) => {
57 | let currentTime = new Date();
58 |
59 | let low = 0, high = birthdays.length-1
60 | let res = 0
61 | while(lowcurrentTime){
64 | res = mid
65 | high = mid - 1
66 | }else{
67 | low = mid + 1
68 | }
69 | }
70 | return res
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/commands/birthday.js:
--------------------------------------------------------------------------------
1 | const Util = require("../utils/utils.js");
2 | const Member = require("../models/Member.js");
3 | const ServerRepository = require("../repositories/server-repository");
4 | const Server = require("../models/Server.js");
5 |
6 | module.exports = {
7 | name: "birthday",
8 | description: "Adds/Edits the user`s birthday",
9 | execute: async (message, args) => {
10 | let item = "";
11 | for (let i = 0; i < args.length; i++) {
12 | item += args[i] + " ";
13 | }
14 | let channel = message.channel;
15 |
16 | if (args === undefined || args.length != 3) {
17 | var embeded = Util.embedMessage("Error",
18 | message.author.tag,"0x232529", "Birthday's format is dd mm yyyy")
19 | channel.send(embeded);
20 | return
21 | }
22 | let dateString = args[1] + " " + args[0] + " " + args[2]
23 | let date = new Date(dateString)
24 | const dbServer = await ServerRepository.findOrCreate(message.guild)
25 |
26 | let member = dbServer.members.find(m => m.user === message.author.tag)
27 | if(member){
28 | let memberIndex = dbServer.members.findIndex(m => m.user === message.author.tag)
29 | dbServer.members[memberIndex].birthday = date
30 | dbServer.members[memberIndex].discord_id = message.author.id
31 | dbServer.markModified('members')
32 | await dbServer.save()
33 | let embededMessage = Util.embedMessage(
34 | 'Birthday Set',
35 | message.author.tag,
36 | '0xffff00',
37 | "Your birthday is modified to " + date
38 | )
39 | channel.send(embededMessage)
40 | } else {
41 | const newBirthday = new Member({
42 | user: message.author.tag,
43 | birthday: date,
44 | discord_id: message.author.id
45 | })
46 | dbServer.members.push(newBirthday)
47 | dbServer.markModified('members')
48 | await dbServer.save()
49 | let embededMessage = Util.embedMessage(
50 | 'Birthday Set',
51 | message.author.tag,
52 | '0xffff00',
53 | "Your birthday is set to " + date
54 | )
55 | channel.send(embededMessage)
56 | }
57 |
58 |
59 |
60 | },
61 | }
62 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behaviour that contributes to creating a positive environment
10 | include:
11 |
12 | - Using welcoming and inclusive language
13 | - Being respectful of differing viewpoints and experiences
14 | - Gracefully accepting constructive criticism
15 | - Focusing on what is best for the community
16 | - Showing empathy towards other community members
17 |
18 | Examples of unacceptable behaviour by participants include:
19 |
20 | - The use of sexualized language or imagery and unwelcome sexual attention or
21 | advances
22 | - Trolling, insulting/derogatory comments, and personal or political attacks
23 | - Public or private harassment
24 | - Publishing others' personal information, such as a physical or electronic
25 | address, without explicit permission
26 | - Other conduct which could reasonably be considered inappropriate in a
27 | professional setting
28 |
29 | ## Our Responsibilities
30 |
31 | Project maintainers are responsible for clarifying the standards of acceptable
32 | behaviour and are expected to take appropriate and fair corrective action in
33 | response to any instances of unacceptable behaviour.
34 |
35 | Project maintainers have the right and responsibility to remove, edit, or
36 | reject comments, commits, code, wiki edits, issues, and other contributions
37 | that are not aligned to this Code of Conduct, or to ban temporarily or
38 | permanently any contributor for other behaviours that they deem inappropriate,
39 | threatening, offensive, or harmful.
40 |
41 | ## Scope
42 |
43 | This Code of Conduct applies both within project spaces and in public spaces
44 | when an individual is representing the project or its community. Examples of
45 | representing a project or community include using an official project e-mail
46 | address, posting via an official social media account or acting as an appointed
47 | representative at an online or offline event. Representation of a project may be
48 | further defined and clarified by project maintainers.
49 |
50 | ## Enforcement
51 |
52 | Instances of abusive, harassing or otherwise unacceptable behaviour may be
53 | reported by contacting the project team at tomassirio@gmail.com. All
54 | complaints will be reviewed and investigated and will result in a response that
55 | is deemed necessary and appropriate to the circumstances. The project team is
56 | obligated to maintain confidentiality concerning the reporter of an incident.
57 | Further details of specific enforcement policies may be posted separately.
58 |
59 | Project maintainers who do not follow or enforce the Code of Conduct in good
60 | faith may face temporary or permanent repercussions as determined by other
61 | members of the project's leadership.
62 |
63 | ## Attribution
64 |
65 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
66 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
67 |
68 | [homepage]: https://www.contributor-covenant.org
69 |
70 | For answers to common questions about this code of conduct, see
71 | https://www.contributor-covenant.org/faq
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | BIRTHDAYBOT
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## :battery: Usage
15 |
16 | Once the bot is running and connected to your Discord channel and database
17 | you can use the following commands:
18 |
19 | ```sh
20 | * ~birthday : adds your birthday to the server
21 | * ~birthdays: brings all the birthdays in the server
22 | * ~nextBirthday: gets the next server's birthday
23 | * ~whatsMyBirthday: gets your birthday
24 | ```
25 |
26 | ## :star: Getting started
27 |
28 | ### :computer: Installation
29 |
30 | Learn how to create a Discord bot and get a valid token [here](https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token).
31 |
32 | ### :floppy_disk: To get started locally, follow these instructions:
33 |
34 | 1. Clone to your local computer using `git`.
35 | 2. Make sure you have Node installed; see instructions [here](https://nodejs.org/en/download/).
36 | 3. Make also sure that you have MongoDB installed; see instructions [here](https://docs.mongodb.com/manual/installation/).
37 | 4. Create a copy of the `.env.example` file named `.env` found in `./src/config/.env.example` and replace the placeholders with your bot token and MongoDB credentials
38 | 5. Run `npm install` to install all dependencies
39 | 6. Run `npm run dev` to start your server as `development` environment or `npm run start` for `production`
40 |
41 |
42 | ## :building_construction: Contribution Guidelines:
43 |
44 | - **_fork_** and **_clone_** this repository
45 | - Make a new branch using `git checkout -b change/username`
46 | - Commit the desired changes to that branch
47 | - Sign off your commits using `git commit -s -m w/signoff`
48 | - Push your changes to the branch and open a pull request
49 |
50 | ### :jack_o_lantern: Contributors Hacktoberfest 2020:
51 |
52 |
98 |
99 |
100 | [Add yours!](./CONTRIBUTING.md)
101 |
102 | ## :bust_in_silhouette: Who Am I?
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------