This repository was archived by the owner on Jan 28, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
268 lines (200 loc) · 10.4 KB
/
main.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
import secrets
import datetime
import random
import json
import re
import bcrypt
import openai
import quora
import paa
from twocaptcha import TwoCaptcha
from tornado.web import RequestHandler, Application, authenticated
from tornado.ioloop import IOLoop
from tornado.concurrent import futures, run_on_executor
from models import User, UserSession, Config, OpenAIQuery, QueueQuestion, QuoraUser, QuoraQuestion
class BaseHandler(RequestHandler):
async def prepare(self):
token = self.get_secure_cookie('token')
if token is not None:
token = token.decode('utf-8')
user_session = await UserSession.get_or_none(token=token).prefetch_related('user', 'user__quora_user')
if user_session is not None:
if datetime.datetime.now() < user_session.expires.replace(tzinfo=None):
self.current_user = user_session.user
else:
# token expired, delete session
await user_session.delete()
return None
class QuoraQueryBaseHandler(BaseHandler):
async def login_quora_client(self, config, quora_user):
result = await self.settings['quora_clients'][quora_user.id].login(quora_user.formkey, quora_user.cookie,
quora_user.email, quora_user.password)
if result.success:
print(f'Successfully logged into Quora on account: {quora_user.email}.')
elif result.errorType == 'failed_captcha':
while not self.settings['quora_clients'][quora_user.id].logged_in:
print(
f'Failed to login to Quora on account due to captcha, retrying with twocaptcha: {quora_user.email}.')
captcha_token = self.settings['twocaptcha_solver'].recaptcha(sitekey=config.recaptcha_sitekey,
url="https://www.quora.com")['code']
print(f'Received recaptcha token from twocaptcha: {captcha_token}')
await self.settings['quora_clients'][quora_user.id].login(quora_user.formkey, quora_user.cookie,
quora_user.email, quora_user.password,
captcha_token)
print(f'Successfully logged into Quora on account: {quora_user.email}.')
else:
print(f'Failed to login to Quora on account: {quora_user.email}.')
async def prepare(self):
await super().prepare()
config = await Config.get_or_none()
if self.settings['twocaptcha_solver'] is None:
self.settings['twocaptcha_solver'] = TwoCaptcha(config.twocaptcha_api_key)
if len(self.settings['quora_clients'].keys()) == 0:
quora_users = await QuoraUser.all()
for user in quora_users:
user_client = quora.Client()
self.settings['quora_clients'][user.id] = user_client
if self.current_user is not None:
self.quora_client = self.settings['quora_clients'][self.current_user.quora_user.id]
if not self.quora_client.logged_in:
await self.login_quora_client(config, self.current_user.quora_user)
class IndexHandler(BaseHandler):
@authenticated
async def get(self):
await self.render('index.html')
class LoginHandler(BaseHandler):
async def get(self):
if self.current_user is not None:
self.redirect('/')
else:
await self.render('login.html', messages=[])
async def post(self):
name = self.get_argument('name')
password = self.get_argument('password')
if name is not None:
user = await User.get_or_none(name=name)
if user is not None:
if bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
# delete all existing sessions
await UserSession.filter(user_id=user.id).delete()
# create new session
expiry = datetime.datetime.now() + datetime.timedelta(
1) # TODO: make token expiration time configurable
user_session = await UserSession.create(token=secrets.token_urlsafe(16), user_id=user.id,
expires=expiry)
print(f'User {user.name} logged in.')
self.set_secure_cookie('token', user_session.token)
self.redirect('/')
return
await self.render('login.html', messages=['Invalid username or password.'])
class LogoutHandler(BaseHandler):
@authenticated
async def get(self):
print(f'User {self.current_user.name} logged out.')
# delete all sessions
await UserSession.filter(user_id=self.current_user.id).delete()
self.clear_cookie('token')
self.redirect('/')
# TODO: move this stuff to its own file
async def generate_openai_questions(config, user, engine, prompt, temperature, max_tokens):
openai.api_key = config.openai_api_key
response = openai.Completion.create(
engine=engine,
prompt=prompt,
temperature=temperature,
max_tokens=max_tokens,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)
openai_query = await OpenAIQuery.create(user=user, engine=engine, prompt=prompt, temperature=temperature,
max_tokens=max_tokens,
response=json.dumps(response))
questions = re.findall(config.openai_question_regex, response['choices'][0]['text'])
for i in range(len(questions)):
questions[i] = " ".join(questions[i].split()[1:])
return (openai_query, questions)
class GenerateQuestionsHandler(BaseHandler):
@authenticated
async def get(self):
await self.render('generate_questions.html', messages=[])
@authenticated
async def post(self):
await self.render('generate_questions.html', messages=[])
engine = self.get_argument('engine')
topic = self.get_argument('topic')
temperature = float(self.get_argument('temperature'))
max_tokens = int(self.get_argument('max_tokens'))
config = await Config.get_or_none()
prompt = config.openai_base_prompt.format(topic)
questions = await generate_openai_questions(config, self.current_user, engine, prompt, temperature, max_tokens)
print(
f'User {self.current_user.name} generated {len(questions[1])} with OpenAI with engine={engine}, temperature={temperature}, max_tokens={max_tokens}, prompt="{prompt}".')
for question in questions[1]:
await QueueQuestion.create(title=question, openai_query=questions[0], approved=False, upload_attempted=False)
class GeneratePAAQuestionsHandler(BaseHandler):
@authenticated
async def get(self):
await self.render('generate_paa_questions.html', messages=[])
@authenticated
async def post(self):
await self.render('generate_paa_questions.html', messages=[])
topic = self.get_argument('topic')
questions = await paa.get_related_questions(topic)
print(f'User {self.current_user.name} generated {len(questions)} questions with PAA with topic="{topic}".')
for question in questions:
await QueueQuestion.create(title=question, openai_query=None, approved=False, upload_attempted=False)
class QuestionsQueueHandler(QuoraQueryBaseHandler):
@authenticated
async def get(self):
questions = await QueueQuestion.filter(approved=False, approved_user=None).all()
await self.render('questions_queue.html', questions=questions)
@authenticated
async def post(self):
id = int(self.get_argument("id"))
approve = True if self.get_argument("approve") == "true" else False
queue_question = await QueueQuestion.get_or_none(id=id)
if queue_question is not None:
queue_question.approved = approve
queue_question.approved_user = self.current_user
await queue_question.save()
self.redirect('/questions_queue')
if approve:
print(f'User {self.current_user.name} approved question: {queue_question.title}.')
else:
print(f'User {self.current_user.name} denied question: {queue_question.title}.')
return
# actually ask on quora
question_response = await self.quora_client.ask_question(queue_question.title)
if question_response.success:
print(f'Succeeded in asking question on Quora: {queue_question.title}.')
await QuoraQuestion.create(id=question_response.question.id, qid=question_response.question.qid,
time_created=datetime.datetime.utcfromtimestamp(
question_response.question.creationTime), queue_question=queue_question)
suggested_topics = await self.quora_client.get_question_suggested_topics(question_response.question.qid)
suggested_tids = [topic.tid for topic in suggested_topics]
applied_topics = await self.quora_client.modify_question_topics(question_response.question.qid,
suggested_tids)
print(f'Applied {len(suggested_tids)} suggested topics for question: {queue_question.title}')
elif question_response.failureType == 106: # TODO: have seperate always running queue for approved questions
print(f'Daily question limit on account has been reached, we are being ratelimited.')
queue_question.approved = False
queue_question.approved_user = None
await queue_question.save()
else:
print(f'Failed to ask question on Quora: {queue_question.title}.')
def make_app(database_uri, secret):
return Application([
(r'/', IndexHandler),
(r'/login', LoginHandler),
(r'/logout', LogoutHandler),
(r'/generate_questions', GenerateQuestionsHandler),
(r'/generate_paa_questions', GeneratePAAQuestionsHandler),
(r'/questions_queue', QuestionsQueueHandler)],
static_path='./static',
template_path='./templates',
login_url='/login',
cookie_secret=secret,
quora_clients={},
twocaptcha_solver=None
)