Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ReneCareenium/rengobot
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: katie-oh/rengobot
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.

Commits on Jun 5, 2023

  1. Copy the full SHA
    ed46055 View commit details
  2. Add files

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    eac1739 View commit details
  3. Update Procfile

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    9a969fb View commit details
  4. Add logs

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    26de394 View commit details
  5. Update gitignore

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    e4929d1 View commit details
  6. Add setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    7cff08b View commit details
  7. Remove setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    a3dc67b View commit details
  8. Update setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    ea40a9f View commit details
  9. Update setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    5fe8376 View commit details
  10. Update setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    b86595b View commit details
  11. Update requirements.txt

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    eb81120 View commit details
  12. Update setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    fc06e2c View commit details
  13. Update setup.py

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    8fd3d10 View commit details
  14. Cleanup

    katie-oh committed Jun 5, 2023
    Copy the full SHA
    d6c4e48 View commit details

Commits on Jun 13, 2023

  1. Who knows

    katie-oh committed Jun 13, 2023
    Copy the full SHA
    dce705c View commit details

Commits on Jun 24, 2023

  1. Don't allow consecutive moves for same color

    Tim Kington committed Jun 24, 2023
    Copy the full SHA
    3950dfb View commit details

Commits on Jul 3, 2023

  1. Add fly

    katie-oh committed Jul 3, 2023
    Copy the full SHA
    e4f6e29 View commit details
  2. Update README.md

    katie-oh authored Jul 3, 2023
    Copy the full SHA
    9e8ee15 View commit details
  3. Create fly.toml

    katie-oh authored Jul 3, 2023
    Copy the full SHA
    8982ae9 View commit details
  4. Merge pull request #2 from katie-oh/documentation

    Documentation
    katie-oh authored Jul 3, 2023
    Copy the full SHA
    dc99ffa View commit details

Commits on Jul 23, 2023

  1. Test

    katie-oh committed Jul 23, 2023
    Copy the full SHA
    98cab4b View commit details
  2. Copy the full SHA
    5e462d1 View commit details
  3. Delete setup

    katie-oh committed Jul 23, 2023
    Copy the full SHA
    ca5efcc View commit details
  4. Merge pull request #4 from katie-oh/docker-debug

    Delete setup
    katie-oh authored Jul 23, 2023
    Copy the full SHA
    80a8baa View commit details
  5. setup

    katie-oh committed Jul 23, 2023
    Copy the full SHA
    b9fb8b8 View commit details
  6. Copy the full SHA
    81a125b View commit details
  7. Copy the full SHA
    d4b39d3 View commit details
  8. Merge pull request #6 from katie-oh/fly

    Add fly
    katie-oh authored Jul 23, 2023
    Copy the full SHA
    0785d2d View commit details
  9. Merge pull request #1 from TimKingtonFC/consecutive-same-color

    Don't allow consecutive moves for same color
    katie-oh authored Jul 23, 2023
    Copy the full SHA
    b8b319e View commit details

Commits on Jul 26, 2023

  1. Fix ids

    katie-oh committed Jul 26, 2023
    Copy the full SHA
    f56f570 View commit details

Commits on Aug 12, 2023

  1. Copy the full SHA
    0d6fd09 View commit details

Commits on Oct 9, 2023

  1. Merge pull request #7 from TimKingtonFC/main

    Added volume, tweaks to startup
    katie-oh authored Oct 9, 2023
    Copy the full SHA
    21b2459 View commit details
Showing with 204 additions and 57 deletions.
  1. +3 −1 .gitignore
  2. +14 −0 Dockerfile
  3. +1 −0 Procfile
  4. +32 −0 README.md
  5. +21 −0 fly.toml
  6. +122 −55 rengobot.py
  7. +2 −0 requirements.txt
  8. +8 −0 setup.py
  9. +0 −1 sgfengine.py
  10. +1 −0 zen-go
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
*.txt
# *.txt
state.txt
token.txt
*.sgf
*.png
*.out
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM liuchong/rustup

RUN apt-get update
RUN apt-get install python3 python3-pip -y
RUN apt-get install -y git

RUN cargo install sgf-render

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .

ENTRYPOINT python3 rengobot.py
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
worker: python rengobot.py
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -6,3 +6,35 @@ A discord bot for playing rengo games!
- python-sgfmill

Make sure to run the bot in an environment with read/write permissions


* installed Rust
* updated requirements.txt
* updated admin and teachers


##Notes:
* If you're testing, be sure to replace the server/channel/account IDs with your own. Otherwise, even though you are running the project locally, it will affect the actual channels! (Ex: there will be duplicate messages, it'll be confused, the state gets weird)

##To run this project locally:
* Create a `requirements.txt` file and add the following lines to it
```
git+[link to your repo]
sgfmill
python-discord
```
* Create a `state.txt` file and add the following text
`[]`
* Create a `token.txt` file and add your Discord bot token to it. [Instructions here](https://www.online-tech-tips.com/computer-tips/what-is-a-discord-token-and-how-to-get-one/#:~:text=To%20get%20a%20Discord%20bot%20token%2C%20you%20first%20have%20to%20create%20a%20bot%3A)
* Go to `rengobot.py` and update the `admins` and `teachers` array with account IDs of your admins/teachers. (I don't think Columbus Go Club uses the teachers function, I actually haven't even looked into what it's for) [Instructions here](https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-)
![image](https://github.com/katie-oh/rengobot/assets/56092878/3d5b8632-4688-4bc8-9268-c8fa4dacef83)
* In `rengobot.py`, update the server ID and channel IDs [Instructions here](https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-)
![image](![image](https://github.com/katie-oh/rengobot/assets/56092878/d86bf8c6-371e-439b-8a42-5f5b136881e3)
* Run `python rengobot.py` in your terminal to spin up your bot! You should see something like this
![image](https://github.com/katie-oh/rengobot/assets/56092878/77231e71-8aab-4b03-80d2-4a16903423e7)

##Deploying
We are currently using fly.io with a Docker image. Once you build your Docker image, you can use your image name in the `fly.toml` file
![image](https://github.com/katie-oh/rengobot/assets/56092878/281e7851-362e-47f3-90b1-9a8e58ca31a8)

Make sure that in your fly.io configuration, that there is no scaling and that the maximum number of instances is 1!
21 changes: 21 additions & 0 deletions fly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# fly.toml app configuration file generated for rengobot on 2023-06-13T09:45:49-04:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = "rengobot"
primary_region = "ord"

[build]
image = "katieoh/rengobot"

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 0

[mounts]
source="rengobot_data"
destination="/data"
177 changes: 122 additions & 55 deletions rengobot.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,79 @@
import os
import ast
import asyncio
import os
import time
from datetime import datetime, timedelta
import asyncio

import discord
from discord.ext import commands, tasks

import sgfengine

import discord
from discord.ext import commands
# import requests
# import raw_input

# We don't use fancy slash commands here. It seems there is this library for python but it looks a bit more involved.
# https://pypi.org/project/discord-py-slash-command/

bot = commands.Bot(command_prefix='$', help_command=None)
# res = requests.get("https://sh.rustup.rs")
# print(res)
# os.system("curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh")
# os.system("cargo install sgf-render")
# os.system("cargo --version")
# raw_input()

intents = discord.Intents.default()
intents.message_content = True
# client = discord.Client(intents=intents)
# bot = commands.Bot(command_prefix='$', intents=intents)
bot = commands.Bot(command_prefix='$', intents=intents, help_command=None)

min_time_player= timedelta(seconds=1) # in random games, min time between same player plays (default days=1)
time_to_skip= timedelta(days=1) # in queue games, how much time to wait for the next move
time_to_skip= timedelta(seconds=1) # in queue games, how much time to wait for the next move
min_players = 2

# People who can start and resign games :O
# Later we might replace this with checking for a role.
admins=[ 756220448118669463, # Young Sun
732403731810877450, # Yeonwoo
145294584077877249, # Mrchance
477895596141707264 # René
admins=[ 463380651467472896, # Devin
907684282145849375, # David
631824578934734848, # Katie
489423695102869535 # Tim
]

teachers=[ 463380651467472896, # Devin
907684282145849375, # David
631824578934734848, # Katie
489423695102869535 # Tim
]

teachers=[ 756220448118669463, # Young Sun
145294584077877249, # Mrchance
732403731810877450] # Yeonwoo
server_id= 1060261462733496320 # Columbus Go Club
# server_id= 1115830515396792342 # Katie's test server
# server_id = 1132772102504726708 # Tim's test server

server_name = "Columbus Go Club"
# server_name = "Tim's Server"

awesome_server_id= 767835617002258443
permitted_channel_ids= [ 875353143867752489, 885498819385634816, 878649716269785150, 881984021192671242, 892435998070431755, 892436145651216444,870604751354613770, 896111390216040540, 896112340657909820, 896112378805116978, 896112602105659442]
permitted_channel_ids= [ 1115612796734943374 ] # zen-go channel
# permitted_channel_ids= [ 1115830516046893109 ] # Katie's test channel
# permitted_channel_ids = [ 1132772102504726710 ] # Tim's test channel

white_stone= "<:white_stone:882731089548939314>"
black_stone= "<:black_stone:882730888453046342>"

with open("token.txt") as f:
token = f.readlines()[0] # Get your own token and put it in token.txt
token = f.readlines()[0].strip() # Get your own token and put it in token.txt

format="%Y_%m_%d_%H_%M_%S_%f"

# The state is a list of tuples (channel_id, "queue"/"random", last_players, last_times, [black_queue, white_queue])

@bot.command()
async def blah(ctx):
await ctx.send("blah")

@bot.command()
async def help(ctx):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
await ctx.send(
'$help : shows this help\n\n'+

@@ -65,7 +93,7 @@ async def help(ctx):

@bot.command()
async def play(ctx, arg):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
user = ctx.author
guild= ctx.guild
@@ -75,7 +103,7 @@ async def play(ctx, arg):

filter_state= [i for i in range(len(state)) if state[i][0] == channel_id] # This is where I should use a fancy next()
if not filter_state:
await ctx.send("No active game in this channel!")
# await ctx.send("No active game in this channel!")
return

i= filter_state[0]
@@ -99,7 +127,11 @@ async def play(ctx, arg):

if len(state[i][2])>0 and state[i][2][-1] == user.id and (state[i][1]!="teachers" or colour=="0"):
await ctx.send("No two consecutive moves by the same player!")
# return
return

if len(state[i][2])>1 and state[i][2][-2] == user.id and (state[i][1]!="teachers" or colour=="0"):
await ctx.send("No two consecutive same-color moves by the same player!")
return

for j in range(len(state[i][2])):
if (state[i][2][j] == user.id and
@@ -147,11 +179,14 @@ async def play(ctx, arg):
else:
await ctx.send(file=file)

colour_text="B" if colour else "W"
await ctx.send(colour_text+ "'s turn!")

with open("state.txt", "w") as f: f.write(repr(state))

@bot.command()
async def edit(ctx, arg): #literally play but with less things
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
# It should wait until the queue has 4 players or so
channel_id= ctx.channel.id
user = ctx.author
@@ -200,7 +235,7 @@ async def edit(ctx, arg): #literally play but with less things

@bot.command()
async def board(ctx):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
user = ctx.author
guild= ctx.guild
@@ -235,7 +270,7 @@ async def board(ctx):

@bot.command()
async def join(ctx):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
user = ctx.author

@@ -268,7 +303,7 @@ async def join(ctx):

@bot.command()
async def leave(ctx):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
user = ctx.author

@@ -299,7 +334,7 @@ async def leave(ctx):

@bot.command()
async def queue(ctx):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
channel= bot.get_channel(channel_id) # thonk the order
guild = channel.guild
@@ -381,13 +416,13 @@ async def queue(ctx):

@bot.command()
async def sgf(ctx):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
file = discord.File(str(ctx.channel.id)+".sgf")
await ctx.send(file=file)

@bot.command()
async def newgame(ctx, gametype, handicap=0, komi=6.5):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
user = ctx.author

@@ -422,7 +457,7 @@ async def newgame(ctx, gametype, handicap=0, komi=6.5):

@bot.command()
async def resign(ctx, arg):
if ctx.guild.id == awesome_server_id and ctx.channel.id not in permitted_channel_ids: return
if ctx.guild.id == server_id and ctx.channel.id not in permitted_channel_ids: return
channel_id= ctx.channel.id
user = ctx.author

@@ -448,50 +483,82 @@ async def resign(ctx, arg):

with open("state.txt", "w") as f: f.write(repr(state))

@tasks.loop(seconds = 10)
async def background_task():
await bot.wait_until_ready()
print("bot ready!")

guild=discord.utils.get(bot.guilds, name="Awesome Baduk")
guild=discord.utils.get(bot.guilds, name=server_name)
game=discord.Game("multiplayer Baduk! $help for command list")
await bot.change_presence(status=discord.Status.online, activity=game)

while not bot.is_closed():
try:
# lowest effort serialization
with open("state.txt") as f: state = ast.literal_eval(f.read())
#print(state)
# print(state)

#TODO find who has to move, skip players accordingly, notify if any has to move
for i in range(len(state)):
if state[i][3] == [] or state[i][1]=="random": continue

channel_id= state[i][0]
channel= bot.get_channel(channel_id)

colour = sgfengine.next_colour(str(channel_id))
if state[i][1]=="teachers" and colour=="1": continue #Ask the teachers if they want a ping

last_time= datetime.strptime(state[i][3][-1],format)
time_left= last_time + time_to_skip-datetime.now()

if time_left < time_to_skip/3.0 and time_left > time_to_skip/3.0-timedelta(seconds=10): # Probably remove? Depends on how passive aggressive it is
next_user = await guild.fetch_member(state[i][4][colour][0])
await channel.send("{}'s turn! Time is running up!".format(next_user.mention))#, time_left.total_seconds()/3600) )
if time_left < timedelta():
state[i][3][-1]= datetime.strftime(datetime.now(),format)
state[i][2][-1]= None
user_id= state[i][4][colour][0]
state[i][4][colour].pop(0)
state[i][4][colour].append(user_id)
next_player=(await guild.fetch_member(state[i][4][colour][0]))
await channel.send(content="{}'s turn! ⭐".format(next_player.mention))
# for i in range(len(state)):
# print("dfgj")
# if state[i][3] == [] or state[i][1]=="random": continue

# channel_id=permitted_channel_ids[0]
# channel= bot.get_channel(channel_id)

# colour = sgfengine.next_colour(str(channel_id))
# if state[i][1]=="teachers" and colour=="1": continue #Ask the teachers if they want a ping

# last_time= datetime.strptime(state[i][3][-1],format)
# time_left= last_time + time_to_skip-datetime.now()

# if time_left < time_to_skip/3.0 and time_left > time_to_skip/3.0-timedelta(seconds=10): # Probably remove? Depends on how passive aggressive it is
# next_user = await guild.fetch_member(state[i][4][colour][0])
# await channel.send("{}'s turn! Time is running up!".format(next_user.mention))#, time_left.total_seconds()/3600) )
# if time_left < timedelta():
# state[i][3][-1]= datetime.strftime(datetime.now(),format)
# state[i][2][-1]= None
# user_id= state[i][4][colour][0]
# state[i][4][colour].pop(0)
# state[i][4][colour].append(user_id)
# next_player=(await guild.fetch_member(state[i][4][colour][0]))
# await channel.send(content="{}'s turn! ⭐".format(next_player.mention))

# for i in range(len(state)):
# if state[i][3] == [] or state[i][1]=="random": continue

channel_id=permitted_channel_ids[0]
channel= bot.get_channel(channel_id)

colour = sgfengine.next_colour(str(channel_id))
# if state[i][1]=="teachers" and colour=="1": continue #Ask the teachers if they want a ping

# last_time= datetime.strptime(state[i][3][-1],format)
# time_left= last_time + time_to_skip-datetime.now()

# if time_left < time_to_skip/3.0 and time_left > time_to_skip/3.0-timedelta(seconds=10): # Probably remove? Depends on how passive aggressive it is
# next_user = await guild.fetch_member(state[i][4][colour][0])
# await channel.send("{}'s turn! Time is running up!".format(next_user.mention))#, time_left.total_seconds()/3600) )
# if time_left < timedelta():
# state[i][3][-1]= datetime.strftime(datetime.now(),format)
# state[i][2][-1]= None
# user_id= state[i][4][colour][0]
# state[i][4][colour].pop(0)
# state[i][4][colour].append(user_id)
# next_player=(await guild.fetch_member(state[i][4][colour][0]))
# await channel.send(content="{}'s turn! ⭐".format(next_player.mention))

with open("state.txt", "w") as f: f.write(repr(state))
await asyncio.sleep(10)

except ConnectionResetError:
print("Connection error")

bot.loop.create_task(background_task())
bot.run(token)

async def main():
os.chdir("/data")
async with bot:
bot.loop.create_task(background_task())
await bot.start(token)

asyncio.run(main())
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sgfmill
python-discord
8 changes: 8 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from setuptools import setup
# from distutils.core import setup

setup(name='ZenGoBot',
version='1.0',
description='Zen Go, forked from AwesomeRengoBot',
py_modules=['sgfengine', 'rengobot'],
)
1 change: 0 additions & 1 deletion sgfengine.py
Original file line number Diff line number Diff line change
@@ -33,7 +33,6 @@ def next_colour(channel_id):
with open(channel_id+".sgf","rb") as f:
game = sgf.Sgf_game.from_bytes(f.read())
f.close()

node= game.get_last_node()
return 1 if ("B" in node.properties() or "AB" in node.properties()) else 0

1 change: 1 addition & 0 deletions zen-go
Submodule zen-go added at 3a894f