ChaterJee 0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chaterjee-0.1/ChaterJee/ChaterJee.py +411 -0
- chaterjee-0.1/ChaterJee/__init__.py +4 -0
- chaterjee-0.1/ChaterJee.egg-info/PKG-INFO +12 -0
- chaterjee-0.1/ChaterJee.egg-info/SOURCES.txt +9 -0
- chaterjee-0.1/ChaterJee.egg-info/dependency_links.txt +1 -0
- chaterjee-0.1/ChaterJee.egg-info/requires.txt +1 -0
- chaterjee-0.1/ChaterJee.egg-info/top_level.txt +1 -0
- chaterjee-0.1/PKG-INFO +12 -0
- chaterjee-0.1/README.md +2 -0
- chaterjee-0.1/setup.cfg +4 -0
- chaterjee-0.1/setup.py +16 -0
@@ -0,0 +1,411 @@
|
|
1
|
+
import os, sys
|
2
|
+
import time
|
3
|
+
from datetime import datetime
|
4
|
+
import urllib.parse
|
5
|
+
import asyncio
|
6
|
+
import pickle
|
7
|
+
import html
|
8
|
+
import traceback
|
9
|
+
import logging, json
|
10
|
+
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo, ReplyKeyboardMarkup, KeyboardButton
|
11
|
+
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, ConversationHandler, filters, PollAnswerHandler, PollHandler
|
12
|
+
from telegram.constants import ParseMode
|
13
|
+
import os.path
|
14
|
+
import threading
|
15
|
+
from subprocess import PIPE, Popen
|
16
|
+
from pathlib import Path
|
17
|
+
|
18
|
+
start_txt = \
|
19
|
+
"""
|
20
|
+
I am ChaterJee, a Research assistant Bot developed by Pallab Dutta in 2025.
|
21
|
+
|
22
|
+
ChaterJee helps you to:
|
23
|
+
- Receive research updates
|
24
|
+
- Start new computational experiments
|
25
|
+
|
26
|
+
even when you are at a remote location.
|
27
|
+
|
28
|
+
"""
|
29
|
+
|
30
|
+
class ChatLogs:
|
31
|
+
def __init__(self, TOKEN, CHATID):
|
32
|
+
self.home = Path.home()
|
33
|
+
self.TOKEN = TOKEN
|
34
|
+
self.CHATID = CHATID
|
35
|
+
self.txt = ''
|
36
|
+
self.fig = ''
|
37
|
+
self.path = os.popen('pwd').read()[:-1]
|
38
|
+
self.smsID = []
|
39
|
+
self.dict = {}
|
40
|
+
self.jobs = {}
|
41
|
+
|
42
|
+
def cmdTRIGGER(self, read_timeout=7, get_updates_read_timeout=42):
|
43
|
+
application = ApplicationBuilder().token(self.TOKEN).read_timeout(read_timeout)\
|
44
|
+
.get_updates_read_timeout(get_updates_read_timeout).build()
|
45
|
+
|
46
|
+
start_handler = CommandHandler('start', self.start)
|
47
|
+
application.add_handler(start_handler)
|
48
|
+
|
49
|
+
fEdit_handler = CommandHandler('edit', self.EditorBabu)
|
50
|
+
application.add_handler(fEdit_handler)
|
51
|
+
|
52
|
+
#jobs_handler = CommandHandler('jobs', self.ShowJobs)
|
53
|
+
#application.add_handler(jobs_handler)
|
54
|
+
|
55
|
+
jobs_handler = ConversationHandler(\
|
56
|
+
entry_points=[CommandHandler("jobs", self.ShowJobs)],\
|
57
|
+
states={
|
58
|
+
0: [MessageHandler(filters.Regex(f"^({'|'.join(list(self.jobs.keys()))})$"), self.StatJobs)],
|
59
|
+
},
|
60
|
+
fallbacks=[CommandHandler("cancel", self.cancel)],
|
61
|
+
)
|
62
|
+
|
63
|
+
application.add_handler(jobs_handler)
|
64
|
+
#add_handler = CommandHandler('add', addDICT)
|
65
|
+
#application.add_handler(add_handler)
|
66
|
+
#run_handler = CommandHandler('run', runCMD)
|
67
|
+
#application.add_handler(run_handler)
|
68
|
+
#com_handler = CommandHandler('com', comCMD)
|
69
|
+
#application.add_handler(com_handler)
|
70
|
+
#exit_handler = CommandHandler('exit', exitCMD)
|
71
|
+
#application.add_handler(exit_handle)
|
72
|
+
#clear_handler = CommandHandler('clear', clsCMD)
|
73
|
+
#application.add_handler(clear_handler)
|
74
|
+
#jedit_handler = CommandHandler('edit', editJSON) # JSON file editor
|
75
|
+
#application.add_handler(jedit_handler)
|
76
|
+
|
77
|
+
application.add_handler(MessageHandler(filters.StatusUpdate.WEB_APP_DATA, self.web_app_data))
|
78
|
+
application.add_handler(MessageHandler(filters.TEXT, self.commands))
|
79
|
+
|
80
|
+
application.run_polling()
|
81
|
+
|
82
|
+
def strSMS(self, update, context):
|
83
|
+
self.smsID.append(update.message.message_id)
|
84
|
+
self.txt=update.message.text
|
85
|
+
cmd = self.txt.split(' ')[0]
|
86
|
+
args = self.txt.split(' ')[1:]
|
87
|
+
if cmd == '/add':
|
88
|
+
self.addDICT(args)
|
89
|
+
elif cmd == '/run':
|
90
|
+
self.cmdRUN(args)
|
91
|
+
elif cmd == '/com':
|
92
|
+
self.cmdCOM(args)
|
93
|
+
elif cmd == '/exit':
|
94
|
+
self.EXIT()
|
95
|
+
elif cmd == '/clear':
|
96
|
+
self.cls()
|
97
|
+
elif cmd[0] == '/':
|
98
|
+
self.txt = 'command not found !'
|
99
|
+
self.sendUPDATE()
|
100
|
+
else:
|
101
|
+
self.txt = "Sorry I can only read '/commands', not 'texts'."
|
102
|
+
self.sendUPDATE()
|
103
|
+
|
104
|
+
def addDICT(self, context):
|
105
|
+
try:
|
106
|
+
key = context[0]
|
107
|
+
eql = context[1]
|
108
|
+
if ('$' in key) and (eql == '='):
|
109
|
+
val = context[2]
|
110
|
+
self.dict[key]=val
|
111
|
+
self.txt = 'added to dictionary'
|
112
|
+
elif ('$' in key) and (eql == '-1'):
|
113
|
+
del self.dict[key]
|
114
|
+
self.txt = 'removed from dictionary'
|
115
|
+
else:
|
116
|
+
self.txt = 'pass the command as:\n/add $key = val'
|
117
|
+
except:
|
118
|
+
self.txt = 'pass the command as:\n/add $key = val'
|
119
|
+
self.sendUPDATE()
|
120
|
+
|
121
|
+
def fmtDICT(self, context):
|
122
|
+
key = []
|
123
|
+
idx = []
|
124
|
+
c = context
|
125
|
+
for i in range(len(context)):
|
126
|
+
w = context[i]
|
127
|
+
if '$' in w:
|
128
|
+
key.append(w)
|
129
|
+
idx.append(i)
|
130
|
+
for j in range(len(idx)):
|
131
|
+
val = self.dict[key[j]]
|
132
|
+
c[idx[j]] = val
|
133
|
+
return c
|
134
|
+
|
135
|
+
#async def cmdRUN(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
136
|
+
def cmdRUN(self, context):
|
137
|
+
context = self.fmtDICT(context)
|
138
|
+
cmd0 = context[0]
|
139
|
+
if cmd0=='cd':
|
140
|
+
cmd1 = context[1]
|
141
|
+
try:
|
142
|
+
os.chdir(cmd1)
|
143
|
+
self.txt=os.popen('pwd').read()
|
144
|
+
except:
|
145
|
+
self.txt='path not found'
|
146
|
+
else:
|
147
|
+
cmd=' '.join(context)
|
148
|
+
try:
|
149
|
+
self.txt=os.popen('%s'%(cmd)).read()
|
150
|
+
except:
|
151
|
+
self.txt='error !'
|
152
|
+
self.sendUPDATE()
|
153
|
+
|
154
|
+
def readout(self):
|
155
|
+
time.sleep(1)
|
156
|
+
fout=open('stdout.txt','r')
|
157
|
+
ferr=open('stderr.txt','r')
|
158
|
+
lout = fout.readlines()
|
159
|
+
out = ' '.join(lout)
|
160
|
+
lerr = ferr.readlines()
|
161
|
+
err = ' '.join(lerr)
|
162
|
+
#out = out.strip()
|
163
|
+
#err = err.strip()
|
164
|
+
if out == '' and err == '':
|
165
|
+
self.txt = 'command executed !'
|
166
|
+
elif out != '' and err == '':
|
167
|
+
self.txt = 'out:\n%s\n'%(out)
|
168
|
+
elif out == '' and err != '':
|
169
|
+
self.txt = 'err:\n%s\n'%(err)
|
170
|
+
else:
|
171
|
+
self.txt = 'out:\n%s\nerr:\n%s\n'%(out,err)
|
172
|
+
print(self.txt)
|
173
|
+
fout.close()
|
174
|
+
ferr.close()
|
175
|
+
tout=open('stdout.txt','w')
|
176
|
+
tout.close()
|
177
|
+
terr=open('stderr.txt','w')
|
178
|
+
terr.close()
|
179
|
+
|
180
|
+
def cmdCOM(self, context):
|
181
|
+
context = self.fmtDICT(context)
|
182
|
+
if context[0]!='i' and context[0]!='kill':
|
183
|
+
self.fout = open('stdout.txt','w')
|
184
|
+
self.ferr = open('stderr.txt','w')
|
185
|
+
self.p=Popen(context,stdin=PIPE,stdout=self.fout,stderr=self.ferr,universal_newlines=True,bufsize=0)
|
186
|
+
self.fout.close()
|
187
|
+
self.ferr.close()
|
188
|
+
elif context[0]=='kill':
|
189
|
+
self.txt = 'process terminated'
|
190
|
+
self.p.terminate()
|
191
|
+
else:
|
192
|
+
comsg = ' '.join(context[1:])
|
193
|
+
self.p.stdin.write(comsg+'\n')
|
194
|
+
self.readout()
|
195
|
+
|
196
|
+
#out,err = self.p.communicate()
|
197
|
+
#out = ' '.join(self.p.stdout)
|
198
|
+
#err = ' '.join(self.p.stderr)
|
199
|
+
#self.txt = "out: \n%s\n\nerr: \n%s"%(out,err)
|
200
|
+
self.sendUPDATE()
|
201
|
+
|
202
|
+
def EXIT(self):
|
203
|
+
os.chdir(self.path)
|
204
|
+
self.txt = "Thanks! I am signing off."
|
205
|
+
self.sendUPDATE()
|
206
|
+
time.sleep(2)
|
207
|
+
self.cls()
|
208
|
+
threading.Thread(target=self.shutdown).start()
|
209
|
+
|
210
|
+
def shutdown(self):
|
211
|
+
self.updater.stop()
|
212
|
+
self.updater.is_idle = False
|
213
|
+
|
214
|
+
def cls(self):
|
215
|
+
for i in self.smsID:
|
216
|
+
self.BOT.delete_message(chat_id=self.CHATID, message_id=i)
|
217
|
+
self.smsID = []
|
218
|
+
|
219
|
+
def sendUPDATE(self):
|
220
|
+
self.BOT.sendChatAction(chat_id=self.CHATID, action="typing")
|
221
|
+
msg = self.BOT.sendMessage(chat_id=self.CHATID, text=self.txt)
|
222
|
+
self.smsID.append(msg.message_id)
|
223
|
+
|
224
|
+
async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
225
|
+
msg = await context.bot.send_message(chat_id=self.CHATID, text=start_txt)
|
226
|
+
self.smsID.append(msg.message_id)
|
227
|
+
|
228
|
+
def register_to_log(self, job_name: str, log_path: str):
|
229
|
+
self.jobs[job_name] = log_path
|
230
|
+
|
231
|
+
async def ShowJobs(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
232
|
+
jobs_file = self.home / ".data" / "JOB_status.json"
|
233
|
+
with open(jobs_file, 'r') as ffr:
|
234
|
+
jobs = json.load(ffr)
|
235
|
+
self.jobs = jobs
|
236
|
+
|
237
|
+
reply_keyboard = [[f'{job}'] for job in list(jobs.keys())]
|
238
|
+
msg = await update.message.reply_text("Select a job to get updates on",\
|
239
|
+
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True, input_field_placeholder="Select the job."\
|
240
|
+
),\
|
241
|
+
)
|
242
|
+
self.smsID.append(msg.message_id)
|
243
|
+
return 0
|
244
|
+
|
245
|
+
async def StatJobs(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
246
|
+
job_name = update.message.text
|
247
|
+
|
248
|
+
jobs_file = self.home / ".data" / "JOB_status.json"
|
249
|
+
with open(jobs_file, 'r') as ffr:
|
250
|
+
jobs = json.load(ffr)
|
251
|
+
self.jobs = jobs
|
252
|
+
|
253
|
+
logDIR = jobs[job_name]['logDIR']
|
254
|
+
logFILE = jobs[job_name]['logFILE']
|
255
|
+
logIMAGE = jobs[job_name]['logIMAGE']
|
256
|
+
|
257
|
+
txt = get_last_line(logDIR / logFILE)
|
258
|
+
if txt is not None:
|
259
|
+
msg = await context.bot.send_message(chat_id=self.CHATID, text=txt)
|
260
|
+
self.smsID.append(msg.message_id)
|
261
|
+
|
262
|
+
try:
|
263
|
+
with open(logDIR / logIMAGE, 'rb') as ffrb:
|
264
|
+
msg = await context.bot.send_photo(chat_id=self.CHATID, photo=ffrb)
|
265
|
+
self.smsID.append(msg.message_id)
|
266
|
+
except:
|
267
|
+
pass
|
268
|
+
|
269
|
+
return ConversationHandler.END
|
270
|
+
|
271
|
+
def get_last_line(filepath):
|
272
|
+
with open(filepath, 'rb') as f:
|
273
|
+
# Go to the end of file
|
274
|
+
f.seek(0, 2)
|
275
|
+
end = f.tell()
|
276
|
+
|
277
|
+
# Step backwards looking for newline
|
278
|
+
pos = end - 1
|
279
|
+
while pos >= 0:
|
280
|
+
f.seek(pos)
|
281
|
+
char = f.read(1)
|
282
|
+
if char == b'\n' and pos != end - 1:
|
283
|
+
break
|
284
|
+
pos -= 1
|
285
|
+
|
286
|
+
# Read from found position to end
|
287
|
+
f.seek(pos + 1)
|
288
|
+
last_line = f.read().decode('utf-8')
|
289
|
+
return last_line.strip()
|
290
|
+
|
291
|
+
async def cancel(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
292
|
+
pass
|
293
|
+
|
294
|
+
async def EditorBabu(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
295
|
+
print('Opening Json file in edit mode')
|
296
|
+
if len(context.args) == 1:
|
297
|
+
file_path = context.args[0]
|
298
|
+
if os.path.exists(file_path):
|
299
|
+
with open(file_path,'r') as ffr:
|
300
|
+
JsonStr = json.load(ffr)
|
301
|
+
encoded_params = urllib.parse.quote(json.dumps(JsonStr))
|
302
|
+
file_name = file_path.split('/')[-1]
|
303
|
+
extender = f"?variables={encoded_params}&fileNAME={file_name}"
|
304
|
+
msg = await update.message.reply_text(
|
305
|
+
"Editor-Babu is opening the Json file.",
|
306
|
+
reply_markup=ReplyKeyboardMarkup.from_button(
|
307
|
+
KeyboardButton(
|
308
|
+
text="Editor Babu",
|
309
|
+
web_app=WebAppInfo(url="https://pallab-dutta.github.io/EditorBabu"+extender),
|
310
|
+
)
|
311
|
+
),
|
312
|
+
)
|
313
|
+
else:
|
314
|
+
txt = f"File {file_path} not Found!"
|
315
|
+
msg = await context.bot.send_message(chat_id=self.CHATID, text=txt)
|
316
|
+
else:
|
317
|
+
txt = "Expected a JSON file as argument. Nothing provided."
|
318
|
+
msg = await context.bot.send_message(chat_id=self.CHATID, text=txt)
|
319
|
+
|
320
|
+
self.smsID.append(msg.message_id)
|
321
|
+
|
322
|
+
async def web_app_data(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None :
|
323
|
+
data = json.loads(update.effective_message.web_app_data.data)
|
324
|
+
formname = data['formNAME']
|
325
|
+
if formname == 'EditorBabu':
|
326
|
+
fileNAME = data['fileNAME']
|
327
|
+
del data['formNAME']
|
328
|
+
del data['fileNAME']
|
329
|
+
if len(data):
|
330
|
+
with open(fileNAME, 'r') as ffr:
|
331
|
+
JSdata = json.load(ffr)
|
332
|
+
JSdata = {**JSdata, **data}
|
333
|
+
with open(fileNAME, 'w') as ffw:
|
334
|
+
json.dump(JSdata, ffw, indent=4)
|
335
|
+
txt = f"edits are saved to {fileNAME}"
|
336
|
+
else:
|
337
|
+
txt = f"No new changes! file kept unchanged."
|
338
|
+
|
339
|
+
msg = await context.bot.send_message(chat_id=self.CHATID, text=txt)
|
340
|
+
self.smsID.append(msg.message_id)
|
341
|
+
|
342
|
+
async def commands(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
343
|
+
self.smsID.append(update.message.message_id)
|
344
|
+
cmd2run = update.message.text.strip()
|
345
|
+
cmd0 = cmd2run.split(' ')[0]
|
346
|
+
if cmd0=='cd':
|
347
|
+
cmd1 = cmd2run[3:]
|
348
|
+
try:
|
349
|
+
os.chdir(cmd1)
|
350
|
+
txt=os.popen('pwd').read()
|
351
|
+
except:
|
352
|
+
txt='path not found'
|
353
|
+
elif cmd0=='clear':
|
354
|
+
for i in self.smsID:
|
355
|
+
await context.bot.delete_message(chat_id=self.CHATID, message_id=i)
|
356
|
+
self.smsID = []
|
357
|
+
txt=''
|
358
|
+
else:
|
359
|
+
print('command: ',cmd2run)
|
360
|
+
cmd=cmd2run
|
361
|
+
try:
|
362
|
+
txt=os.popen('%s'%(cmd)).read()
|
363
|
+
except:
|
364
|
+
txt='error !'
|
365
|
+
if len(txt):
|
366
|
+
msg = await context.bot.send_message(chat_id=self.CHATID, text=txt)
|
367
|
+
self.smsID.append(msg.message_id)
|
368
|
+
|
369
|
+
|
370
|
+
class NoteLogs:
|
371
|
+
def __init__(self):
|
372
|
+
self.home = Path.home()
|
373
|
+
self.jobNAME = None
|
374
|
+
self.logDIR = None
|
375
|
+
self.logFILE = None
|
376
|
+
self.logIMAGE = None
|
377
|
+
|
378
|
+
def write(self, jobNAME: str, logDIR: str = None, logSTRING: str = None, logFILE: str = 'log_file.out', logIMAGE: str = 'log_file.png'):
|
379
|
+
if logDIR is None:
|
380
|
+
pwd = Path.cwd()
|
381
|
+
_logDIR = pwd / jobNAME
|
382
|
+
_logDIR.mkdir(exist_ok=True)
|
383
|
+
logDIR = str(pwd)
|
384
|
+
else:
|
385
|
+
_logDIR = Path(logDIR)
|
386
|
+
|
387
|
+
with open(_logDIR / logFILE, 'a') as ffa:
|
388
|
+
print(f"\n{logSTRING}",file=ffa)
|
389
|
+
|
390
|
+
_logFILE = _logDIR / logFILE
|
391
|
+
_logIMAGE = _logDIR / logIMAGE
|
392
|
+
|
393
|
+
self.jobNAME = jobNAME
|
394
|
+
self.logDIR = logDIR
|
395
|
+
self.logFILE = logFILE
|
396
|
+
self.logIMAGE = logIMAGE
|
397
|
+
self.save_job_JSON()
|
398
|
+
|
399
|
+
def save_job_JSON(self):
|
400
|
+
jobs_file = self.home / ".data" / "JOB_status.json"
|
401
|
+
with open(jobs_file, 'r') as ffr:
|
402
|
+
jobs = json.load(ffr)
|
403
|
+
jobs[self.jobNAME] = {\
|
404
|
+
"logDIR": self.logDIR, \
|
405
|
+
"logFILE": self.logFILE, \
|
406
|
+
"logIMAGE": self.logIMAGE \
|
407
|
+
}
|
408
|
+
with open(jobs_file, 'w') as ffw:
|
409
|
+
json.dump(jobs, ffw, indent=4)
|
410
|
+
|
411
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: ChaterJee
|
3
|
+
Version: 0.1
|
4
|
+
Summary: Communicate your project updates via Telegram Bot!
|
5
|
+
Author: Pallab Dutta
|
6
|
+
Author-email: pallab9997@gmail.com
|
7
|
+
Requires-Python: >=3.8
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
Requires-Dist: python-telegram-bot==20.7
|
10
|
+
|
11
|
+
# ChaterJee
|
12
|
+
A Telegram ChatBot to Parse Research Project Updates
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
python-telegram-bot==20.7
|
@@ -0,0 +1 @@
|
|
1
|
+
ChaterJee
|
chaterjee-0.1/PKG-INFO
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: ChaterJee
|
3
|
+
Version: 0.1
|
4
|
+
Summary: Communicate your project updates via Telegram Bot!
|
5
|
+
Author: Pallab Dutta
|
6
|
+
Author-email: pallab9997@gmail.com
|
7
|
+
Requires-Python: >=3.8
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
Requires-Dist: python-telegram-bot==20.7
|
10
|
+
|
11
|
+
# ChaterJee
|
12
|
+
A Telegram ChatBot to Parse Research Project Updates
|
chaterjee-0.1/README.md
ADDED
chaterjee-0.1/setup.cfg
ADDED
chaterjee-0.1/setup.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from setuptools import setup, find_packages
|
2
|
+
|
3
|
+
setup(
|
4
|
+
name='ChaterJee',
|
5
|
+
version='0.1',
|
6
|
+
packages=find_packages(),
|
7
|
+
install_requires=[
|
8
|
+
'python-telegram-bot==20.7',
|
9
|
+
],
|
10
|
+
author='Pallab Dutta',
|
11
|
+
author_email='pallab9997@gmail.com',
|
12
|
+
description='Communicate your project updates via Telegram Bot!',
|
13
|
+
long_description=open("README.md").read(),
|
14
|
+
long_description_content_type="text/markdown",
|
15
|
+
python_requires=">=3.8",
|
16
|
+
)
|