forked from aaronrausch/chess-puzzles-twitter-bot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chess_puzzle_twitter_bot.py
189 lines (142 loc) · 5.6 KB
/
chess_puzzle_twitter_bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import chess
import chess.svg
from svglib.svglib import svg2rlg # Convert CVG to PDF
from reportlab.graphics import renderPDF
import fitz # Convert PDF to PNG
import tweepy
import os
from csv import reader
from random import choice
from authorization_tokens import *
import schedule
import time
def twitter_authorization():
twitter_auth = {
'CONSUMER_KEY': consumer_key,
'CONSUMER_KEY_SECRET': consumer_key_secret,
'ACCESS_KEY': access_key,
'ACCESS_SECRET': access_key_secret
}
auth = tweepy.OAuthHandler(
twitter_auth['CONSUMER_KEY'], twitter_auth['CONSUMER_KEY_SECRET'])
auth.set_access_token(
twitter_auth['ACCESS_KEY'], twitter_auth['ACCESS_SECRET'])
chess_api = tweepy.API(auth)
try:
chess_api.verify_credentials()
print("Authentication succeeded.")
except:
print("Error during authentication.")
exit()
return chess_api
def choose_puzzle():
print('Choosing puzzle...')
# Imports full list of puzzles
with open('lichess_puzzles.csv') as puzzles_csv:
puzzles = reader(puzzles_csv)
puzzles = list(puzzles)
puzzle = choice(puzzles)
return puzzle
def create_puzzle_dictionary(puzzle):
'''Creates a dictionary with all necessary puzzle values.'''
return {'PUZZLE_CODE': puzzle[0],
'PUZZLE_FEN': puzzle[1],
'FIRST_MOVE': puzzle[2].split()[0],
'FIRST_PUZZLE_MOVE': 'White' if puzzle[1].split()[-5] == 'b' else 'Black',
'PUZZLE_RATING': puzzle[3],
'PUZZLE_URL': puzzle[8]
}
def play_first_move(puzzle_fen, move):
'''Plays the first move, and updates the board state.
This has the effect of setting up the puzzle for the position to solve.
'''
board = chess.Board(puzzle_fen)
move = chess.Move.from_uci(move)
board.push(move)
return board
def board_to_svg(board, move):
''' Creates SVG of puzzle.
The color pallette is inspired by Lichess.com.
'''
# Checks the last move played
last_move = chess.Move(chess.parse_square(
move[0:2]), chess.parse_square(move[2:4]))
board_svg = chess.svg.board(board, size=900, lastmove=last_move, flipped=not board.turn, coordinates=False,
colors={
'square light': '#DFE3E6',
'square dark': '#90A2AC',
'square light lastmove': '#C7D7A0',
'square dark lastmove': '#99B07E'}
)
return board_svg
def svg_to_pdf(board_svg, name):
'''This function converts the SVG to PDF.
I would convert the SVG to PNG directly, but due to the complexity of the chess SVG,
there were frequent glitches. Convering to PDF first, then using a different library
to convert to PNG, solves this problem.
'''
with open(f"{name}.svg", "w") as puzzle_svg:
# Saves PDF to directory
puzzle_svg.write(board_svg)
# Converts SVG to PDF
chess_pdf = svg2rlg(f'{name}.svg')
renderPDF.drawToFile(chess_pdf, f'{name}.pdf')
# Delete SVG
os.remove(f'{name}.svg')
def pdf_to_image(chess_pdf, name):
'''This function converts the PDF to a PNG file,
a format of media accepted by Twitter.
'''
chess_doc = fitz.open(chess_pdf)
# 'Iterating' through the one-page document, and generating a map
for page in chess_doc:
# (3, 3) Guarantees high-resolution
matrix = fitz.Matrix(3, 3)
puzzle_picture = page.get_pixmap(matrix=matrix)
puzzle_picture.save(f"{name}.png")
# Delete PDF, as it is no longer needed
os.remove(f'{name}.pdf')
def prepare_tweet(chess_api, name, first_puzzle_move, puzzle_rating, puzzle_url):
'''Prepares and uploads tweet, returns confirmation,
and cleans up main directory.
'''
# Upload puzzle image to Twitter
chess_puzzle_media = chess_api.media_upload(f'{name}.png')
# Create text for post
tweet = f'''
{first_puzzle_move} to move. This puzzle is rated {puzzle_rating} on Lichess.org.
Thank you to Lichess for providing the puzzle database.
Puzzle Details: {puzzle_url}'''
# Post tweet with image
chess_api.update_status(status=tweet, media_ids=[
chess_puzzle_media.media_id])
# Move image to 'posted_chess_puzzles'
os.replace(f"{name}.png",
f"posted_chess_puzzles/{name}.png")
return print('Puzzle successfully posted.')
def main():
# Authorizes ChessTwitterBot account
chess_api = twitter_authorization()
puzzle = choose_puzzle()
# Generates a dictionary with all important puzzle information
puzzle_dictionary = create_puzzle_dictionary(puzzle)
puzzle_code, puzzle_fen, first_move, first_puzzle_move, puzzle_rating, puzzle_url = puzzle_dictionary.values()
# Create file name for puzzle
file_name = f'{puzzle_code}_{puzzle_rating}'
# Plays the move before puzzle
board = play_first_move(puzzle_fen, first_move)
# Generates board SVG, including highlighting the previous move
board_csv = board_to_svg(board, first_move)
# Converts SVG -> PDF -> PNG
svg_to_pdf(board_csv, file_name)
pdf_to_image(f'{file_name}.pdf', file_name)
# Tweets
prepare_tweet(chess_api, file_name, first_puzzle_move,
puzzle_rating, puzzle_url)
if __name__ == '__main__':
schedule.every().hour.do(lambda: main())
while True:
schedule.run_pending()
time.sleep(15)
print("Bot Running..")
time.sleep(15)