Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meme bot cog #8

Open
wants to merge 18 commits into
base: meme_bot
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.idea/

*.pyc
/logs/

.env
__pycache__/
90 changes: 54 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
<h1 align="center">DecodED 3</h1>

> 👋 Hello and welcome to DecodED 3! Over the next 6 weeks, we will be creating a discord bot with [Discord.py](https://discordpy.readthedocs.io/en/stable/index.html)

<h2>Demo</h2>

* TODO add gif/screenshot of Discord bot in action
> 👋 Hello and welcome to DecodED 3! Over the next 5 weeks, we will be creating a discord bot with [Discord.py](https://discordpy.readthedocs.io/en/stable/index.html)

---

Expand All @@ -14,27 +10,31 @@

- [About DecodED](#about-decoded)
- [Dates](#dates)
- [Zoom Link: https://unimelb.zoom.us/j/88528442813?pwd=WlYrQ3pHcm5xMXpGQkZZSllZZTNvQT09](#zoom-link-httpsunimelbzoomusj88528442813pwdwlyrq3phcm5xmxpgqkzzsllzztnvqt09)
- [About this repository](#about-this-repository)
- [Workshop 1 - Introduction to Discord.py](#workshop-1---introduction-to-discordpy)
- [Workshop 2 - Tic Tac Toe](#workshop-2---tic-tac-toe)
- [Workshop 3 - Polling Bot](#workshop-3---polling-bot)
- [Workshop 4 - Meme Bot](#workshop-4---meme-bot)
- [Workshop 5 - Music Bot](#workshop-5---music-bot)
- [Workshop 2 - Meme Bot](#workshop-2---meme-bot)
- [Workshop 3 - Music Bot](#workshop-3---music-bot)
- [Workshop 4 - Polling Bot](#workshop-4---polling-bot)
- [Workshop 5 - Tic Tac Toe](#workshop-5---tic-tac-toe)

</details>

---

## About DecodED
* DecodED is a series of workshops run every year by HackMelbourne to help students/teach students with introductory programming background to/how to... TODO
* This year we are trying a new teaching style with DecodED so let us know if you like it TODO add a feedback link

* DecodED is a series of workshops run every year by HackMelbourne to help students/teach students with introductory
programming background to create some cool things with code. In past years, we created full-stack websites and a Space Invaders game. This year, we will be creating a Discord bot!
* We are also trying a new teaching style this year so let us know if you like it!

### Dates

Lesson | Location | People | Date
| -- | -- | -- | -- |
Foundations (Basic) | Alan Gilbert 101 and Zoom | Xin Yu | 2pm, Wednesday 24th August
Meme bot (easy) | Alan Gilbert G20 and Zoom | Aly, Minh | Wednesday 31st August
Music bot | Alan Gilbert 121 (Theatre) and Zoom | Aryan, Ryan | Wednesday 7th September
Foundations (Basic) | Alan Gilbert 101 and Zoom | Xin Yu | 2pm, Wednesday 24th August
Meme bot (easy) | Zoom | Aly, Minh | Wednesday 31st August
Music bot | Alan Gilbert 121 (Theatre) and Zoom | Aryan, Ryan | Wednesday 7th September
Poll | Alan Gilbert G20 and Zoom | Jerry, Hoan | Wednesday 14th September
TicTacToe bot | Alan Gilbert 103 and Zoom | Warren, Daniel | 2pm, Wednesday 21st September

Expand All @@ -44,41 +44,59 @@ Password: 323737
## About this repository
* This repository contains:
* Participant Workbook
* each workbook is made up of multiple Parts which are then made up of multiple **✅ Tasks** that the participant should complete, with **🧩 Hints** that help guide them in completing the Task, and **💡 Extensions** that are optional tasks that can be done if the participant has completed the Task ahead of schedule
* The flow:
1. host presents/teaches about Part 1
2. participants follow the workbook to complete the ✅ Tasks in Part 1 of the workbook - participants who are ahead of the tasks can start do the 💡 Extensions and participants who are behind can use the 🧩 Hints or ask for help
3. once most of the participants are done with Part 1, host starts presenting/teaching Part 2 and the cycle continues
* Slides (if any)
* each workbook is made up of multiple Parts which are then made up of multiple **✅ Tasks** that the participant
should complete, with **🧩 Hints** that help guide them in completing the Task, and **💡 Extensions** that are
optional tasks that can be done if the participant has completed the Task ahead of schedule
* Workshop Recordings
* Code for the Discord Bot being created

## Workshop 1 - Introduction to Discord.py
> About DecodED3, Covers basics of Discord.py, create a basic bot that says "Hello, World!", learn about the basic structure of bots,
* [📔Participant Workbook](/w1/README.md)
* [🐍Python Cheatsheet](/w1/python_cheatsheet.md)
* [🐍Python Setup](/w1/python_setup.md)
* [👾Discord.py Cheatsheet](/w1/discord_py_cheatsheet.md)
* [🔗Discord.py Documentation](https://discordpy.readthedocs.io/en/stable/index.html)
* [Workshop Recording] TBD!

> About DecodED3, Covers basics of Discord.py, create a basic bot that says "Hello, World!", learn about the basic
> structure of Discord bots.
>
> Hosted by Xin Yu

* [📔Participant Workbook](https://github.com/HackMelbourne/Decoded-3/tree/foundations-bot)
* [Workshop Recording] Coming Soon!

## Workshop 2 - Meme Bot
> Who doesn’t love a good meme? Join us and create the functionality to meme on your friends so hard that they wished they had their own meme bot.
* Aly and Minh

> Who doesn’t love a good meme? Join us and create the functionality to meme on your friends so hard that they wished
> they had their own meme bot.
>
> Hosted by Aly and Minh

* [📔Participant Workbook](https://github.com/HackMelbourne/Decoded-3/tree/meme_bot)
* [Workshop Recording]

## Workshop 3 - Music Bot
> Ever since YouTube banned music bots, discord servers have been desperately lacking some tunes. Impress your friends by bringing them back by building your own bot with the ability to play music, plus additional music controls! Now you can resume your lo-fi beat study sessions with your mates!

* Aryan and Ryan
> Ever since YouTube banned music bots, discord servers have been desperately lacking some tunes. Impress your friends
> by bringing them back by building your own bot with the ability to play music, plus additional music controls! Now you
> can resume your lo-fi beat study sessions with your mates!
>
> Hosted by Aryan and Ryan

* [📔Participant Workbook](https://github.com/HackMelbourne/Decoded-3/tree/music-bot)
* [Workshop Recording]

## Workshop 4 - Polling Bot
> Caught up in your server arguing why Minecraft is (definitively) the best game? Why not run a poll on your server and prove your friends wrong? Learn to build your own polling bot to settle your arguments in style 😎

* Jerry and Hoan
> Caught up in your server arguing why Minecraft is (definitively) the best game? Why not run a poll on your server and
> prove your friends wrong? Learn to build your own polling bot to settle your arguments in style 😎
>
> Jerry and Hoan

## Workshop 5 - Tic Tac Toe
> Fancy a game, but don’t want to leave your friends in discord? In this lesson, you’ll learn to implement a tic tac toe game within the Discord bot so you can vs your friends whenever you wish!
* [📔Participant Workbook](https://github.com/HackMelbourne/Decoded-3/tree/poll_system)
* [Workshop Recording]

* Warren and Weng Jae (Daniel)
## Workshop 5 - Tic Tac Toe

> Fancy a game, but don’t want to leave your friends in discord? In this lesson, you’ll learn to implement a tic tac toe
> game within the Discord bot so you can vs your friends whenever you wish!
>
> Warren and Weng Jae (Daniel)

* [📔Participant Workbook](https://github.com/HackMelbourne/Decoded-3/tree/tictactoe)
* [Workshop Recording]
22 changes: 22 additions & 0 deletions cogless.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import discord
import os

from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('TOKEN')

client = discord.Client(intents=discord.Intents.all())

@client.event
async def on_ready():
print(f'We have logged in as {client.user}')

@client.event
async def on_message(msg):
# print(msg.content)
if msg.author == client.user:
return
if msg.content.startswith('$hello'):
await msg.channel.send(f'Hello, {msg.author.name}!')

client.run(TOKEN)
20 changes: 10 additions & 10 deletions cogs/hello.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from discord.ext import commands

class Hello(commands.Cog):

class Hello(commands.Cog): # 👈 cog class called `Hello`
def __init__(self, client):
self.client = client # defining bot as global var in class
self.client = client

@commands.Cog.listener() # this is a decorator for events/listeners
@commands.Cog.listener() # 👈 this is a decorator for events/listeners
async def on_ready(self):
print(f'We have logged in as {self.client.user}')


@commands.command() # this is for making a command
async def hello(self, ctx):
print(ctx)
@commands.command() # this is for making a command
async def hello(self, ctx): # 👈 called when messages contain `!hello`
await ctx.send(f'Hi!')

def setup(bot): # a extension must have a setup function
bot.add_cog(Hello(bot)) # adding a cog


async def setup(bot): # 👈 a extension must have a setup function
await bot.add_cog(Hello(bot)) # 👈 adding the cog
152 changes: 152 additions & 0 deletions cogs/meme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import json
import os

import requests
from discord.ext import commands
from dotenv import load_dotenv
from pymongo import MongoClient
from requests.exceptions import HTTPError
from schema import Schema, SchemaError, And, Regex

load_dotenv()

TOKEN = os.getenv('TOKEN')
COMMAND = os.getenv('DISCORD_COMMAND_ROOT')
BOTNAME = os.getenv('BOT_NAME')
API_ROOT = os.getenv('MEME_API_ROOT')
MONGODB_URI = os.getenv('MONGODB_URI')
DB = os.getenv('DB')
COLLECTION = os.getenv('COLLECTION')

collection = MongoClient(MONGODB_URI)[DB][COLLECTION]


# all commands that this bot will respond to will begin with ";;bot_name"
# bot = commands.Bot(command_prefix=COMMAND + BOTNAME, strip_after_prefix=True, intents=discord.Intents.all())

def get_random_memes(count):
try:
response = requests.get(f"{API_ROOT}{count}")
except HTTPError as error:
raise error
json_data = json.loads(response.text)
return json_data


def get_random_meme_from_subreddits(subReddit, count):
try:
response = requests.get(f"{API_ROOT}{subReddit}/{count}")
except HTTPError as error:
raise error
if (response.status_code == 404 or response.status_code == 400):
raise HTTPError(response.text)
json_data = json.loads(response.text)
return json_data


def check_link_die(link):
try:
response = requests.get(link)
except:
return False
if (response.status_code == 404):
return False
return True


meme_schema = Schema({
"name": str,
"url": And(str, Regex("^(https:\/\/i.redd.it\/)\w+\.\w{3}$"))})


class Meme(commands.Cog):
def __init__(self, bot):
self.bot = bot

@commands.command()
async def meme(self, ctx, *args):
# ;;random
if (len(args) == 0):
random_meme = get_random_memes(1)['memes'][0]
while (check_link_die(random_meme['url']) == False):
random_meme = get_random_memes(1)['memes'][0]
await ctx.send(random_meme['url'])

# ;;random 5
if (len(args) > 0):
try:
random_memes = get_random_memes(int(args[0]))['memes']
except:
await ctx.send("Unrecognized command. Pretty sure that wasn't a number :)")
pass
random_memes_image_link = [e['url'] for e in random_memes]
for link in random_memes_image_link:
if (check_link_die(link) == False):
continue
else:
await ctx.send(link)
pass

@commands.command()
async def meme_subreddit(self, ctx, arg1):
subreddit = arg1
try:
random_meme = get_random_meme_from_subreddits(subreddit, 1)
random_meme_subreddit = random_meme['memes'][0]
while (check_link_die(random_meme_subreddit['url']) == False):
random_meme = get_random_meme_from_subreddits(subreddit, 1)
random_meme_subreddit = random_meme['memes'][0]
await ctx.send(random_meme_subreddit['url'])
except HTTPError as err:
await ctx.send('Subreddit not found')
pass
pass

@commands.command()
async def save_meme(self, ctx, arg1):
try:
response = await ctx.channel.fetch_message(ctx.message.reference.message_id)
meme_url = response.content
except print(0):
await ctx.send("You forgot to reply to a meme, or the meme you replied to wasn't saved in the right format")
pass
saved_name = arg1
saved_meme = {
"name": saved_name,
"url": meme_url,
}
try:
meme_schema.validate(saved_meme)
except SchemaError as err:
await ctx.send(err)
pass
collection.insert_one(saved_meme)
await ctx.send("Saved to database!")
pass

@commands.command()
async def load_meme(self, ctx, arg1):
lookup_name = arg1
try:
found_meme = collection.find_one({"name": lookup_name})
await ctx.send(found_meme["url"])
except TypeError:
await ctx.send("Meme not found in database!")
pass
pass

@commands.command()
async def delete_meme(self, ctx, arg1):
lookup_name = arg1
try:
found_meme = collection.find_one({"name": lookup_name})
except TypeError:
await ctx.send("Meme not found in database!")
pass
collection.delete_one(found_meme)
await ctx.send("Meme was deleted from database!")
pass


async def setup(bot):
await bot.add_cog(Meme(bot))
25 changes: 10 additions & 15 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import discord
import os

from dotenv import load_dotenv
from discord.ext import commands
import discord
import asyncio

load_dotenv()
TOKEN = os.getenv('TOKEN')

client = discord.Client()

@client.event
async def on_ready():
print(f'We have logged in as {client.user}')
client = commands.Bot(command_prefix = "!", intents = discord.Intents.all()) # instead of a client, we create a Bot instance

@client.event
async def on_message(msg):
# print(msg.content)
if msg.author == client.user:
return
if msg.content.startswith('$hello'):
await msg.channel.send(f'Hello, {msg.author.name}!')
# 👇 Looks inside the /cogs/ folder and loads up all of our cogs
for filename in os.listdir("./cogs"):
if filename.endswith(".py"):
asyncio.run(client.load_extension("cogs." + filename[:-3])) # calls the cog's `setup()` function

client.run(TOKEN)
client.run(TOKEN)
Loading