ChaterJee 0.1__py3-none-any.whl

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/ChaterJee.py ADDED
@@ -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
+
ChaterJee/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .ChaterJee import ChatLogs, NoteLogs
2
+
3
+ __version__ = "0.1"
4
+ __author__ = "Pallab Dutta (2025)"
@@ -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,6 @@
1
+ ChaterJee/ChaterJee.py,sha256=P6MA9F86RCQzfAAv23vNc8TTtIgkOgjJY4XuzCrJUXo,14392
2
+ ChaterJee/__init__.py,sha256=tZmkZY2XbzJ8rHDh8nOmb1W73kPecPv3xcEiCPwkZf4,98
3
+ chaterjee-0.1.dist-info/METADATA,sha256=Kxc2tw95cjEMXwouA1iIKO5AVAuWo77zAwdDhG2cABU,338
4
+ chaterjee-0.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
5
+ chaterjee-0.1.dist-info/top_level.txt,sha256=Z1UAYoaNybpDiKjqa1yFpti_q0FNECVItb3-9yAh3gM,10
6
+ chaterjee-0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.3.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ ChaterJee