flowtask 5.8.4__cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.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.
Files changed (470) hide show
  1. flowtask/__init__.py +93 -0
  2. flowtask/__main__.py +38 -0
  3. flowtask/bots/__init__.py +6 -0
  4. flowtask/bots/check.py +93 -0
  5. flowtask/bots/codebot.py +51 -0
  6. flowtask/components/ASPX.py +148 -0
  7. flowtask/components/AddDataset.py +352 -0
  8. flowtask/components/Amazon.py +523 -0
  9. flowtask/components/AutoTask.py +314 -0
  10. flowtask/components/Azure.py +80 -0
  11. flowtask/components/AzureUsers.py +106 -0
  12. flowtask/components/BaseAction.py +91 -0
  13. flowtask/components/BaseLoop.py +198 -0
  14. flowtask/components/BestBuy.py +800 -0
  15. flowtask/components/CSVToGCS.py +120 -0
  16. flowtask/components/CompanyScraper/__init__.py +1 -0
  17. flowtask/components/CompanyScraper/parsers/__init__.py +6 -0
  18. flowtask/components/CompanyScraper/parsers/base.py +102 -0
  19. flowtask/components/CompanyScraper/parsers/explorium.py +192 -0
  20. flowtask/components/CompanyScraper/parsers/leadiq.py +206 -0
  21. flowtask/components/CompanyScraper/parsers/rocket.py +133 -0
  22. flowtask/components/CompanyScraper/parsers/siccode.py +109 -0
  23. flowtask/components/CompanyScraper/parsers/visualvisitor.py +130 -0
  24. flowtask/components/CompanyScraper/parsers/zoominfo.py +118 -0
  25. flowtask/components/CompanyScraper/scrapper.py +1054 -0
  26. flowtask/components/CopyTo.py +177 -0
  27. flowtask/components/CopyToBigQuery.py +243 -0
  28. flowtask/components/CopyToMongoDB.py +291 -0
  29. flowtask/components/CopyToPg.py +609 -0
  30. flowtask/components/CopyToRethink.py +207 -0
  31. flowtask/components/CreateGCSBucket.py +102 -0
  32. flowtask/components/CreateReport/CreateReport.py +228 -0
  33. flowtask/components/CreateReport/__init__.py +9 -0
  34. flowtask/components/CreateReport/charts/__init__.py +15 -0
  35. flowtask/components/CreateReport/charts/bar.py +51 -0
  36. flowtask/components/CreateReport/charts/base.py +66 -0
  37. flowtask/components/CreateReport/charts/pie.py +64 -0
  38. flowtask/components/CreateReport/utils.py +9 -0
  39. flowtask/components/CustomerSatisfaction.py +196 -0
  40. flowtask/components/DataInput.py +200 -0
  41. flowtask/components/DateList.py +255 -0
  42. flowtask/components/DbClient.py +163 -0
  43. flowtask/components/DialPad.py +146 -0
  44. flowtask/components/DocumentDBQuery.py +200 -0
  45. flowtask/components/DownloadFrom.py +371 -0
  46. flowtask/components/DownloadFromD2L.py +113 -0
  47. flowtask/components/DownloadFromFTP.py +181 -0
  48. flowtask/components/DownloadFromIMAP.py +315 -0
  49. flowtask/components/DownloadFromS3.py +198 -0
  50. flowtask/components/DownloadFromSFTP.py +265 -0
  51. flowtask/components/DownloadFromSharepoint.py +110 -0
  52. flowtask/components/DownloadFromSmartSheet.py +114 -0
  53. flowtask/components/DownloadS3File.py +229 -0
  54. flowtask/components/Dummy.py +59 -0
  55. flowtask/components/DuplicatePhoto.py +411 -0
  56. flowtask/components/EmployeeEvaluation.py +237 -0
  57. flowtask/components/ExecuteSQL.py +323 -0
  58. flowtask/components/ExtractHTML.py +178 -0
  59. flowtask/components/FileBase.py +178 -0
  60. flowtask/components/FileCopy.py +181 -0
  61. flowtask/components/FileDelete.py +82 -0
  62. flowtask/components/FileExists.py +146 -0
  63. flowtask/components/FileIteratorDelete.py +112 -0
  64. flowtask/components/FileList.py +194 -0
  65. flowtask/components/FileOpen.py +75 -0
  66. flowtask/components/FileRead.py +120 -0
  67. flowtask/components/FileRename.py +106 -0
  68. flowtask/components/FilterIf.py +284 -0
  69. flowtask/components/FilterRows/FilterRows.py +200 -0
  70. flowtask/components/FilterRows/__init__.py +10 -0
  71. flowtask/components/FilterRows/functions.py +4 -0
  72. flowtask/components/GCSToBigQuery.py +103 -0
  73. flowtask/components/GoogleA4.py +150 -0
  74. flowtask/components/GoogleGeoCoding.py +344 -0
  75. flowtask/components/GooglePlaces.py +315 -0
  76. flowtask/components/GoogleSearch.py +539 -0
  77. flowtask/components/HTTPClient.py +268 -0
  78. flowtask/components/ICIMS.py +146 -0
  79. flowtask/components/IF.py +179 -0
  80. flowtask/components/IcimsFolderCopy.py +173 -0
  81. flowtask/components/ImageFeatures/__init__.py +5 -0
  82. flowtask/components/ImageFeatures/process.py +233 -0
  83. flowtask/components/IteratorBase.py +251 -0
  84. flowtask/components/LangchainLoader/__init__.py +5 -0
  85. flowtask/components/LangchainLoader/loader.py +194 -0
  86. flowtask/components/LangchainLoader/loaders/__init__.py +22 -0
  87. flowtask/components/LangchainLoader/loaders/abstract.py +362 -0
  88. flowtask/components/LangchainLoader/loaders/basepdf.py +50 -0
  89. flowtask/components/LangchainLoader/loaders/docx.py +91 -0
  90. flowtask/components/LangchainLoader/loaders/html.py +119 -0
  91. flowtask/components/LangchainLoader/loaders/pdfblocks.py +146 -0
  92. flowtask/components/LangchainLoader/loaders/pdfmark.py +79 -0
  93. flowtask/components/LangchainLoader/loaders/pdftables.py +135 -0
  94. flowtask/components/LangchainLoader/loaders/qa.py +67 -0
  95. flowtask/components/LangchainLoader/loaders/txt.py +55 -0
  96. flowtask/components/LeadIQ.py +650 -0
  97. flowtask/components/Loop.py +253 -0
  98. flowtask/components/Lowes.py +334 -0
  99. flowtask/components/MS365Usage.py +156 -0
  100. flowtask/components/MSTeamsMessages.py +320 -0
  101. flowtask/components/MarketClustering.py +1051 -0
  102. flowtask/components/MergeFiles.py +362 -0
  103. flowtask/components/MilvusOutput.py +87 -0
  104. flowtask/components/NearByStores.py +175 -0
  105. flowtask/components/NetworkNinja/__init__.py +6 -0
  106. flowtask/components/NetworkNinja/models/__init__.py +52 -0
  107. flowtask/components/NetworkNinja/models/abstract.py +177 -0
  108. flowtask/components/NetworkNinja/models/account.py +39 -0
  109. flowtask/components/NetworkNinja/models/client.py +19 -0
  110. flowtask/components/NetworkNinja/models/district.py +14 -0
  111. flowtask/components/NetworkNinja/models/events.py +101 -0
  112. flowtask/components/NetworkNinja/models/forms.py +499 -0
  113. flowtask/components/NetworkNinja/models/market.py +16 -0
  114. flowtask/components/NetworkNinja/models/organization.py +34 -0
  115. flowtask/components/NetworkNinja/models/photos.py +125 -0
  116. flowtask/components/NetworkNinja/models/project.py +44 -0
  117. flowtask/components/NetworkNinja/models/region.py +28 -0
  118. flowtask/components/NetworkNinja/models/store.py +203 -0
  119. flowtask/components/NetworkNinja/models/user.py +151 -0
  120. flowtask/components/NetworkNinja/router.py +854 -0
  121. flowtask/components/Odoo.py +175 -0
  122. flowtask/components/OdooInjector.py +192 -0
  123. flowtask/components/OpenFromXML.py +126 -0
  124. flowtask/components/OpenWeather.py +41 -0
  125. flowtask/components/OpenWithBase.py +616 -0
  126. flowtask/components/OpenWithPandas.py +715 -0
  127. flowtask/components/PGPDecrypt.py +199 -0
  128. flowtask/components/PandasIterator.py +187 -0
  129. flowtask/components/PandasToFile.py +189 -0
  130. flowtask/components/Paradox.py +339 -0
  131. flowtask/components/ParamIterator.py +117 -0
  132. flowtask/components/ParseHTML.py +84 -0
  133. flowtask/components/PlacerStores.py +249 -0
  134. flowtask/components/Pokemon.py +507 -0
  135. flowtask/components/PositiveBot.py +62 -0
  136. flowtask/components/PowerPointSlide.py +400 -0
  137. flowtask/components/PrintMessage.py +127 -0
  138. flowtask/components/ProductCompetitors/__init__.py +5 -0
  139. flowtask/components/ProductCompetitors/parsers/__init__.py +7 -0
  140. flowtask/components/ProductCompetitors/parsers/base.py +72 -0
  141. flowtask/components/ProductCompetitors/parsers/bestbuy.py +86 -0
  142. flowtask/components/ProductCompetitors/parsers/lowes.py +103 -0
  143. flowtask/components/ProductCompetitors/scrapper.py +155 -0
  144. flowtask/components/ProductCompliant.py +169 -0
  145. flowtask/components/ProductInfo/__init__.py +1 -0
  146. flowtask/components/ProductInfo/parsers/__init__.py +5 -0
  147. flowtask/components/ProductInfo/parsers/base.py +83 -0
  148. flowtask/components/ProductInfo/parsers/brother.py +97 -0
  149. flowtask/components/ProductInfo/parsers/canon.py +167 -0
  150. flowtask/components/ProductInfo/parsers/epson.py +118 -0
  151. flowtask/components/ProductInfo/parsers/hp.py +131 -0
  152. flowtask/components/ProductInfo/parsers/samsung.py +97 -0
  153. flowtask/components/ProductInfo/scraper.py +319 -0
  154. flowtask/components/ProductPricing.py +118 -0
  155. flowtask/components/QS.py +261 -0
  156. flowtask/components/QSBase.py +201 -0
  157. flowtask/components/QueryIterator.py +273 -0
  158. flowtask/components/QueryToInsert.py +327 -0
  159. flowtask/components/QueryToPandas.py +432 -0
  160. flowtask/components/RESTClient.py +195 -0
  161. flowtask/components/RethinkDBQuery.py +189 -0
  162. flowtask/components/Rsync.py +74 -0
  163. flowtask/components/RunSSH.py +59 -0
  164. flowtask/components/RunShell.py +71 -0
  165. flowtask/components/SalesForce.py +20 -0
  166. flowtask/components/SaveImageBank/__init__.py +257 -0
  167. flowtask/components/SchedulingVisits.py +592 -0
  168. flowtask/components/ScrapPage.py +216 -0
  169. flowtask/components/ScrapSearch.py +79 -0
  170. flowtask/components/SendNotify.py +257 -0
  171. flowtask/components/SentimentAnalysis.py +694 -0
  172. flowtask/components/ServiceScrapper/__init__.py +5 -0
  173. flowtask/components/ServiceScrapper/parsers/__init__.py +1 -0
  174. flowtask/components/ServiceScrapper/parsers/base.py +94 -0
  175. flowtask/components/ServiceScrapper/parsers/costco.py +93 -0
  176. flowtask/components/ServiceScrapper/scrapper.py +199 -0
  177. flowtask/components/SetVariables.py +156 -0
  178. flowtask/components/SubTask.py +182 -0
  179. flowtask/components/SuiteCRM.py +48 -0
  180. flowtask/components/Switch.py +175 -0
  181. flowtask/components/TableBase.py +148 -0
  182. flowtask/components/TableDelete.py +312 -0
  183. flowtask/components/TableInput.py +143 -0
  184. flowtask/components/TableOutput/TableOutput.py +384 -0
  185. flowtask/components/TableOutput/__init__.py +3 -0
  186. flowtask/components/TableSchema.py +534 -0
  187. flowtask/components/Target.py +223 -0
  188. flowtask/components/ThumbnailGenerator.py +156 -0
  189. flowtask/components/ToPandas.py +67 -0
  190. flowtask/components/TransformRows/TransformRows.py +507 -0
  191. flowtask/components/TransformRows/__init__.py +9 -0
  192. flowtask/components/TransformRows/functions.py +559 -0
  193. flowtask/components/TransposeRows.py +176 -0
  194. flowtask/components/UPCDatabase.py +86 -0
  195. flowtask/components/UnGzip.py +171 -0
  196. flowtask/components/Uncompress.py +172 -0
  197. flowtask/components/UniqueRows.py +126 -0
  198. flowtask/components/Unzip.py +107 -0
  199. flowtask/components/UpdateOperationalVars.py +147 -0
  200. flowtask/components/UploadTo.py +299 -0
  201. flowtask/components/UploadToS3.py +136 -0
  202. flowtask/components/UploadToSFTP.py +160 -0
  203. flowtask/components/UploadToSharepoint.py +205 -0
  204. flowtask/components/UserFunc.py +122 -0
  205. flowtask/components/VivaTracker.py +140 -0
  206. flowtask/components/WSDLClient.py +123 -0
  207. flowtask/components/Wait.py +18 -0
  208. flowtask/components/Walmart.py +199 -0
  209. flowtask/components/Workplace.py +134 -0
  210. flowtask/components/XMLToPandas.py +267 -0
  211. flowtask/components/Zammad/__init__.py +41 -0
  212. flowtask/components/Zammad/models.py +0 -0
  213. flowtask/components/ZoomInfoScraper.py +409 -0
  214. flowtask/components/__init__.py +104 -0
  215. flowtask/components/abstract.py +18 -0
  216. flowtask/components/flow.py +530 -0
  217. flowtask/components/google.py +335 -0
  218. flowtask/components/group.py +221 -0
  219. flowtask/components/py.typed +0 -0
  220. flowtask/components/reviewscrap.py +132 -0
  221. flowtask/components/tAutoincrement.py +117 -0
  222. flowtask/components/tConcat.py +109 -0
  223. flowtask/components/tExplode.py +119 -0
  224. flowtask/components/tFilter.py +184 -0
  225. flowtask/components/tGroup.py +236 -0
  226. flowtask/components/tJoin.py +270 -0
  227. flowtask/components/tMap/__init__.py +9 -0
  228. flowtask/components/tMap/functions.py +54 -0
  229. flowtask/components/tMap/tMap.py +450 -0
  230. flowtask/components/tMelt.py +112 -0
  231. flowtask/components/tMerge.py +114 -0
  232. flowtask/components/tOrder.py +93 -0
  233. flowtask/components/tPandas.py +94 -0
  234. flowtask/components/tPivot.py +71 -0
  235. flowtask/components/tPluckCols.py +76 -0
  236. flowtask/components/tUnnest.py +82 -0
  237. flowtask/components/user.py +401 -0
  238. flowtask/conf.py +457 -0
  239. flowtask/download.py +102 -0
  240. flowtask/events/__init__.py +11 -0
  241. flowtask/events/events/__init__.py +20 -0
  242. flowtask/events/events/abstract.py +95 -0
  243. flowtask/events/events/alerts/__init__.py +362 -0
  244. flowtask/events/events/alerts/colfunctions.py +131 -0
  245. flowtask/events/events/alerts/functions.py +158 -0
  246. flowtask/events/events/dummy.py +12 -0
  247. flowtask/events/events/exec.py +124 -0
  248. flowtask/events/events/file/__init__.py +7 -0
  249. flowtask/events/events/file/base.py +51 -0
  250. flowtask/events/events/file/copy.py +23 -0
  251. flowtask/events/events/file/delete.py +16 -0
  252. flowtask/events/events/interfaces/__init__.py +9 -0
  253. flowtask/events/events/interfaces/client.py +67 -0
  254. flowtask/events/events/interfaces/credentials.py +28 -0
  255. flowtask/events/events/interfaces/notifications.py +58 -0
  256. flowtask/events/events/jira.py +122 -0
  257. flowtask/events/events/log.py +26 -0
  258. flowtask/events/events/logerr.py +52 -0
  259. flowtask/events/events/notify.py +59 -0
  260. flowtask/events/events/notify_event.py +160 -0
  261. flowtask/events/events/publish.py +54 -0
  262. flowtask/events/events/sendfile.py +104 -0
  263. flowtask/events/events/task.py +97 -0
  264. flowtask/events/events/teams.py +98 -0
  265. flowtask/events/events/webhook.py +58 -0
  266. flowtask/events/manager.py +287 -0
  267. flowtask/exceptions.c +39393 -0
  268. flowtask/exceptions.cpython-39-x86_64-linux-gnu.so +0 -0
  269. flowtask/extensions/__init__.py +3 -0
  270. flowtask/extensions/abstract.py +82 -0
  271. flowtask/extensions/logging/__init__.py +65 -0
  272. flowtask/hooks/__init__.py +9 -0
  273. flowtask/hooks/actions/__init__.py +22 -0
  274. flowtask/hooks/actions/abstract.py +66 -0
  275. flowtask/hooks/actions/dummy.py +23 -0
  276. flowtask/hooks/actions/jira.py +74 -0
  277. flowtask/hooks/actions/rest.py +320 -0
  278. flowtask/hooks/actions/sampledata.py +37 -0
  279. flowtask/hooks/actions/sensor.py +23 -0
  280. flowtask/hooks/actions/task.py +9 -0
  281. flowtask/hooks/actions/ticket.py +37 -0
  282. flowtask/hooks/actions/zammad.py +55 -0
  283. flowtask/hooks/hook.py +62 -0
  284. flowtask/hooks/models.py +17 -0
  285. flowtask/hooks/service.py +187 -0
  286. flowtask/hooks/step.py +91 -0
  287. flowtask/hooks/types/__init__.py +23 -0
  288. flowtask/hooks/types/base.py +129 -0
  289. flowtask/hooks/types/brokers/__init__.py +11 -0
  290. flowtask/hooks/types/brokers/base.py +54 -0
  291. flowtask/hooks/types/brokers/mqtt.py +35 -0
  292. flowtask/hooks/types/brokers/rabbitmq.py +82 -0
  293. flowtask/hooks/types/brokers/redis.py +83 -0
  294. flowtask/hooks/types/brokers/sqs.py +44 -0
  295. flowtask/hooks/types/fs.py +232 -0
  296. flowtask/hooks/types/http.py +49 -0
  297. flowtask/hooks/types/imap.py +200 -0
  298. flowtask/hooks/types/jira.py +279 -0
  299. flowtask/hooks/types/mail.py +205 -0
  300. flowtask/hooks/types/postgres.py +98 -0
  301. flowtask/hooks/types/responses/__init__.py +8 -0
  302. flowtask/hooks/types/responses/base.py +5 -0
  303. flowtask/hooks/types/sharepoint.py +288 -0
  304. flowtask/hooks/types/ssh.py +141 -0
  305. flowtask/hooks/types/tagged.py +59 -0
  306. flowtask/hooks/types/upload.py +85 -0
  307. flowtask/hooks/types/watch.py +71 -0
  308. flowtask/hooks/types/web.py +36 -0
  309. flowtask/interfaces/AzureClient.py +137 -0
  310. flowtask/interfaces/AzureGraph.py +839 -0
  311. flowtask/interfaces/Boto3Client.py +326 -0
  312. flowtask/interfaces/DropboxClient.py +173 -0
  313. flowtask/interfaces/ExcelHandler.py +94 -0
  314. flowtask/interfaces/FTPClient.py +131 -0
  315. flowtask/interfaces/GoogleCalendar.py +201 -0
  316. flowtask/interfaces/GoogleClient.py +133 -0
  317. flowtask/interfaces/GoogleDrive.py +127 -0
  318. flowtask/interfaces/GoogleGCS.py +89 -0
  319. flowtask/interfaces/GoogleGeocoding.py +93 -0
  320. flowtask/interfaces/GoogleLang.py +114 -0
  321. flowtask/interfaces/GooglePub.py +61 -0
  322. flowtask/interfaces/GoogleSheet.py +68 -0
  323. flowtask/interfaces/IMAPClient.py +137 -0
  324. flowtask/interfaces/O365Calendar.py +113 -0
  325. flowtask/interfaces/O365Client.py +220 -0
  326. flowtask/interfaces/OneDrive.py +284 -0
  327. flowtask/interfaces/Outlook.py +155 -0
  328. flowtask/interfaces/ParrotBot.py +130 -0
  329. flowtask/interfaces/SSHClient.py +378 -0
  330. flowtask/interfaces/Sharepoint.py +496 -0
  331. flowtask/interfaces/__init__.py +36 -0
  332. flowtask/interfaces/azureauth.py +119 -0
  333. flowtask/interfaces/cache.py +201 -0
  334. flowtask/interfaces/client.py +82 -0
  335. flowtask/interfaces/compress.py +525 -0
  336. flowtask/interfaces/credentials.py +124 -0
  337. flowtask/interfaces/d2l.py +239 -0
  338. flowtask/interfaces/databases/__init__.py +5 -0
  339. flowtask/interfaces/databases/db.py +223 -0
  340. flowtask/interfaces/databases/documentdb.py +55 -0
  341. flowtask/interfaces/databases/rethink.py +39 -0
  342. flowtask/interfaces/dataframes/__init__.py +11 -0
  343. flowtask/interfaces/dataframes/abstract.py +21 -0
  344. flowtask/interfaces/dataframes/arrow.py +71 -0
  345. flowtask/interfaces/dataframes/dt.py +69 -0
  346. flowtask/interfaces/dataframes/pandas.py +167 -0
  347. flowtask/interfaces/dataframes/polars.py +60 -0
  348. flowtask/interfaces/db.py +263 -0
  349. flowtask/interfaces/env.py +46 -0
  350. flowtask/interfaces/func.py +137 -0
  351. flowtask/interfaces/http.py +1780 -0
  352. flowtask/interfaces/locale.py +40 -0
  353. flowtask/interfaces/log.py +75 -0
  354. flowtask/interfaces/mask.py +143 -0
  355. flowtask/interfaces/notification.py +154 -0
  356. flowtask/interfaces/playwright.py +339 -0
  357. flowtask/interfaces/powerpoint.py +368 -0
  358. flowtask/interfaces/py.typed +0 -0
  359. flowtask/interfaces/qs.py +376 -0
  360. flowtask/interfaces/result.py +87 -0
  361. flowtask/interfaces/selenium_service.py +779 -0
  362. flowtask/interfaces/smartsheet.py +154 -0
  363. flowtask/interfaces/stat.py +39 -0
  364. flowtask/interfaces/task.py +96 -0
  365. flowtask/interfaces/template.py +118 -0
  366. flowtask/interfaces/vectorstores/__init__.py +1 -0
  367. flowtask/interfaces/vectorstores/abstract.py +133 -0
  368. flowtask/interfaces/vectorstores/milvus.py +669 -0
  369. flowtask/interfaces/zammad.py +107 -0
  370. flowtask/models.py +193 -0
  371. flowtask/parsers/__init__.py +15 -0
  372. flowtask/parsers/_yaml.c +11978 -0
  373. flowtask/parsers/_yaml.cpython-39-x86_64-linux-gnu.so +0 -0
  374. flowtask/parsers/argparser.py +235 -0
  375. flowtask/parsers/base.c +15155 -0
  376. flowtask/parsers/base.cpython-39-x86_64-linux-gnu.so +0 -0
  377. flowtask/parsers/json.c +11968 -0
  378. flowtask/parsers/json.cpython-39-x86_64-linux-gnu.so +0 -0
  379. flowtask/parsers/maps.py +49 -0
  380. flowtask/parsers/toml.c +11968 -0
  381. flowtask/parsers/toml.cpython-39-x86_64-linux-gnu.so +0 -0
  382. flowtask/plugins/__init__.py +16 -0
  383. flowtask/plugins/components/__init__.py +0 -0
  384. flowtask/plugins/handler/__init__.py +45 -0
  385. flowtask/plugins/importer.py +31 -0
  386. flowtask/plugins/sources/__init__.py +0 -0
  387. flowtask/runner.py +283 -0
  388. flowtask/scheduler/__init__.py +9 -0
  389. flowtask/scheduler/functions.py +493 -0
  390. flowtask/scheduler/handlers/__init__.py +8 -0
  391. flowtask/scheduler/handlers/manager.py +504 -0
  392. flowtask/scheduler/handlers/models.py +58 -0
  393. flowtask/scheduler/handlers/service.py +72 -0
  394. flowtask/scheduler/notifications.py +65 -0
  395. flowtask/scheduler/scheduler.py +993 -0
  396. flowtask/services/__init__.py +0 -0
  397. flowtask/services/bots/__init__.py +0 -0
  398. flowtask/services/bots/telegram.py +264 -0
  399. flowtask/services/files/__init__.py +11 -0
  400. flowtask/services/files/manager.py +522 -0
  401. flowtask/services/files/model.py +37 -0
  402. flowtask/services/files/service.py +767 -0
  403. flowtask/services/jira/__init__.py +3 -0
  404. flowtask/services/jira/jira_actions.py +191 -0
  405. flowtask/services/tasks/__init__.py +13 -0
  406. flowtask/services/tasks/launcher.py +213 -0
  407. flowtask/services/tasks/manager.py +323 -0
  408. flowtask/services/tasks/service.py +275 -0
  409. flowtask/services/tasks/task_manager.py +376 -0
  410. flowtask/services/tasks/tasks.py +155 -0
  411. flowtask/storages/__init__.py +16 -0
  412. flowtask/storages/exceptions.py +12 -0
  413. flowtask/storages/files/__init__.py +8 -0
  414. flowtask/storages/files/abstract.py +29 -0
  415. flowtask/storages/files/filesystem.py +66 -0
  416. flowtask/storages/tasks/__init__.py +19 -0
  417. flowtask/storages/tasks/abstract.py +26 -0
  418. flowtask/storages/tasks/database.py +33 -0
  419. flowtask/storages/tasks/filesystem.py +108 -0
  420. flowtask/storages/tasks/github.py +119 -0
  421. flowtask/storages/tasks/memory.py +45 -0
  422. flowtask/storages/tasks/row.py +25 -0
  423. flowtask/tasks/__init__.py +0 -0
  424. flowtask/tasks/abstract.py +526 -0
  425. flowtask/tasks/command.py +118 -0
  426. flowtask/tasks/pile.py +486 -0
  427. flowtask/tasks/py.typed +0 -0
  428. flowtask/tasks/task.py +778 -0
  429. flowtask/template/__init__.py +161 -0
  430. flowtask/tests.py +257 -0
  431. flowtask/types/__init__.py +8 -0
  432. flowtask/types/typedefs.c +11347 -0
  433. flowtask/types/typedefs.cpython-39-x86_64-linux-gnu.so +0 -0
  434. flowtask/utils/__init__.py +24 -0
  435. flowtask/utils/constants.py +117 -0
  436. flowtask/utils/encoders.py +21 -0
  437. flowtask/utils/executor.py +112 -0
  438. flowtask/utils/functions.cpp +14280 -0
  439. flowtask/utils/functions.cpython-39-x86_64-linux-gnu.so +0 -0
  440. flowtask/utils/json.cpp +13349 -0
  441. flowtask/utils/json.cpython-39-x86_64-linux-gnu.so +0 -0
  442. flowtask/utils/mail.py +63 -0
  443. flowtask/utils/parseqs.c +13324 -0
  444. flowtask/utils/parserqs.cpython-39-x86_64-linux-gnu.so +0 -0
  445. flowtask/utils/stats.py +308 -0
  446. flowtask/utils/transformations.py +74 -0
  447. flowtask/utils/uv.py +12 -0
  448. flowtask/utils/validators.py +97 -0
  449. flowtask/version.py +11 -0
  450. flowtask-5.8.4.dist-info/LICENSE +201 -0
  451. flowtask-5.8.4.dist-info/METADATA +209 -0
  452. flowtask-5.8.4.dist-info/RECORD +470 -0
  453. flowtask-5.8.4.dist-info/WHEEL +6 -0
  454. flowtask-5.8.4.dist-info/entry_points.txt +3 -0
  455. flowtask-5.8.4.dist-info/top_level.txt +2 -0
  456. plugins/components/CreateQR.py +39 -0
  457. plugins/components/TestComponent.py +28 -0
  458. plugins/components/Use1.py +13 -0
  459. plugins/components/Workplace.py +117 -0
  460. plugins/components/__init__.py +3 -0
  461. plugins/sources/__init__.py +0 -0
  462. plugins/sources/get_populartimes.py +78 -0
  463. plugins/sources/google.py +150 -0
  464. plugins/sources/hubspot.py +679 -0
  465. plugins/sources/icims.py +679 -0
  466. plugins/sources/mobileinsight.py +501 -0
  467. plugins/sources/newrelic.py +262 -0
  468. plugins/sources/uap.py +268 -0
  469. plugins/sources/venu.py +244 -0
  470. plugins/sources/vocinity.py +314 -0
@@ -0,0 +1,839 @@
1
+ import asyncio
2
+ from typing import Any, Optional, List, Dict
3
+ from datetime import datetime, timedelta
4
+ from azure.identity.aio import (
5
+ ClientSecretCredential,
6
+ OnBehalfOfCredential
7
+ )
8
+ import msal
9
+ from msgraph import GraphServiceClient
10
+ from msgraph.generated.models.o_data_errors.o_data_error import ODataError
11
+ from msgraph.generated.models.chat_message import ChatMessage
12
+ from msgraph.generated.models.chat_message_collection_response import (
13
+ ChatMessageCollectionResponse
14
+ )
15
+ from msgraph.generated.teams.item.channels.get_all_messages.get_all_messages_request_builder import (
16
+ GetAllMessagesRequestBuilder
17
+ )
18
+ from msgraph.generated.chats.item.messages.messages_request_builder import (
19
+ MessagesRequestBuilder
20
+ )
21
+ from msgraph.generated.users.users_request_builder import (
22
+ UsersRequestBuilder
23
+ )
24
+ from kiota_abstractions.base_request_configuration import RequestConfiguration
25
+ from navconfig.logging import logging
26
+ from .client import ClientInterface
27
+ from ..conf import (
28
+ MS_TEAMS_TENANT_ID,
29
+ MS_TEAMS_CLIENT_ID,
30
+ MS_TEAMS_CLIENT_SECRET,
31
+ DEFAULT_TEAMS_USER
32
+ )
33
+ from ..exceptions import ComponentError, ConfigError
34
+
35
+
36
+ logging.getLogger('msal').setLevel(logging.INFO)
37
+ logging.getLogger('azure').setLevel(logging.WARNING)
38
+
39
+ DEFAULT_SCOPES = ["https://graph.microsoft.com/.default"]
40
+
41
+
42
+ def generate_auth_string(user, token):
43
+ return f"user={user}\x01Auth=Bearer {token}\x01\x01"
44
+
45
+
46
+ class AzureGraph(ClientInterface):
47
+ """
48
+ AzureGraph.
49
+
50
+ Overview
51
+
52
+ Authentication and authorization Using Azure Identity and Microsoft Graph.
53
+ """
54
+ _credentials: dict = {
55
+ "tenant_id": str,
56
+ "client_id": str,
57
+ "client_secret": str,
58
+ "user": str,
59
+ "password": str
60
+ }
61
+
62
+ def __init__(
63
+ self,
64
+ tenant_id: str = None,
65
+ client_id: str = None,
66
+ client_secret: str = None,
67
+ scopes: list = None,
68
+ **kwargs,
69
+ ) -> None:
70
+ self.tenant_id = tenant_id or MS_TEAMS_TENANT_ID
71
+ # credentials:
72
+ self.client_id = client_id or MS_TEAMS_CLIENT_ID
73
+ self.client_secret = client_secret or MS_TEAMS_CLIENT_SECRET
74
+ # User delegated credentials:
75
+ self.user = kwargs.pop('user', None)
76
+ self.password = kwargs.pop('password', None)
77
+ self.user_credentials = None
78
+ # scopes:
79
+ self.scopes = scopes if scopes is not None else DEFAULT_SCOPES
80
+ kwargs['no_host'] = True
81
+ kwargs['credentials'] = kwargs.get(
82
+ "credentials", {
83
+ "client_id": self.client_id,
84
+ "tenant_id": self.tenant_id,
85
+ "client_secret": self.client_secret,
86
+ "user": self.user,
87
+ "password": self.password
88
+ }
89
+ )
90
+ super(AzureGraph, self).__init__(
91
+ **kwargs
92
+ )
93
+ self._client = None
94
+ self._graph = None
95
+ self.token_uri = f"https://login.microsoftonline.com/{self.tenant_id}/oauth2/v2.0/token"
96
+ self.graph_uri = "https://graph.microsoft.com/v1.0"
97
+ # Logging:
98
+ if not hasattr(self, '_logger'):
99
+ self._logger = logging.getLogger('AzureGraph')
100
+
101
+ @property
102
+ def graph(self):
103
+ return self._graph
104
+
105
+ @property
106
+ def client(self):
107
+ return self._client
108
+
109
+ ## Override the Async-Context:
110
+ async def __aenter__(self) -> "AzureGraph":
111
+ return self
112
+
113
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
114
+ # clean up anything you need to clean up
115
+ return self.close()
116
+
117
+ def get_client(self, kind: str = 'client_credentials', token: str = None):
118
+ if not self.credentials:
119
+ raise ConfigError(
120
+ "Azure Graph: Credentials are required to create a client."
121
+ )
122
+ tenant_id = self.credentials.get('tenant_id', self.tenant_id)
123
+ client_id = self.credentials.get('client_id', self.client_id)
124
+ client_secret = self.credentials.get('client_secret', self.client_secret)
125
+ # fix the token URL
126
+ self.token_uri = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
127
+ client = None
128
+ # TODO: other type of clients
129
+ if kind == 'client_credentials':
130
+ client = ClientSecretCredential(
131
+ tenant_id=tenant_id,
132
+ client_id=client_id,
133
+ client_secret=client_secret
134
+ )
135
+ elif kind == 'on_behalf_of':
136
+ client = OnBehalfOfCredential(
137
+ client_id=client_id,
138
+ client_secret=client_secret,
139
+ tenant_id=tenant_id,
140
+ user_assertion=token
141
+ )
142
+ return client
143
+
144
+ def get_graph_client(self, client: Any, token: str = None, scopes: Optional[list] = None):
145
+ if not scopes:
146
+ scopes = self.scopes
147
+ return GraphServiceClient(credentials=client, scopes=scopes)
148
+
149
+ async def get_token(self):
150
+ """
151
+ Retrieves an access token for Microsoft Graph API using ClientSecretCredential.
152
+ """
153
+ if not self._client:
154
+ self._client = self.get_client()
155
+ tenant_id = self.credentials.get('tenant_id', self.tenant_id)
156
+ try:
157
+ # Use the credential to obtain an access token
158
+ token = await self._client.get_token(
159
+ self.scopes[0],
160
+ tenant_id=tenant_id
161
+ )
162
+ self._logger.info(
163
+ "Access token retrieved successfully."
164
+ )
165
+ return token.token, token
166
+ except Exception as e:
167
+ self._logger.error(
168
+ f"Failed to retrieve access token: {e}"
169
+ )
170
+ raise ComponentError(
171
+ f"Could not obtain access token: {e}"
172
+ )
173
+
174
+ async def get_user_info(self, user_principal_name: str) -> dict:
175
+ """
176
+ Fetches user information from Microsoft Graph API based on userPrincipalName.
177
+
178
+ Args:
179
+ user_principal_name (str): The user principal name (UPN) of the user to fetch info for.
180
+
181
+ Returns:
182
+ dict: User information as a dictionary.
183
+ """
184
+ try:
185
+ if not self._graph:
186
+ raise ComponentError(
187
+ "Graph client not initialized. Please call 'open' first."
188
+ )
189
+
190
+ # Fetch the user info using the Graph client
191
+ user_info = await self._graph.users[user_principal_name].get()
192
+ self._logger.info(
193
+ f"Retrieved information for user: {user_principal_name}"
194
+ )
195
+ return user_info
196
+ except Exception as e:
197
+ self._logger.error(
198
+ f"Failed to retrieve user info for {user_principal_name}: {e}"
199
+ )
200
+ raise ComponentError(f"Could not retrieve user info: {e}")
201
+
202
+ def user_auth(self, username: str, password: str, scopes: list = None) -> dict:
203
+ tenant_id = self.credentials.get('tenant_id', self.tenant_id)
204
+ authority_url = f'https://login.microsoftonline.com/{tenant_id}'
205
+ client_id = self.credentials.get("client_id", self.client_id)
206
+
207
+ if not scopes:
208
+ scopes = ["https://graph.microsoft.com/.default"]
209
+ app = msal.PublicClientApplication(
210
+ authority=authority_url,
211
+ client_id=client_id,
212
+ client_credential=None
213
+ )
214
+ result = app.acquire_token_by_username_password(
215
+ username,
216
+ password,
217
+ scopes=scopes
218
+ )
219
+ if "access_token" not in result:
220
+ error_message = result.get('error_description', 'Unknown error')
221
+ error_code = result.get('error', 'Unknown error code')
222
+ raise RuntimeError(
223
+ f"Failed to obtain access token: {error_code} - {error_message}"
224
+ )
225
+ return result
226
+
227
+ def close(self, timeout: int = 1):
228
+ self._client = None
229
+ self._graph = None
230
+
231
+ def open(self, **kwargs) -> "AzureGraph":
232
+ """open.
233
+ Starts (open) a connection to Microsoft Graph Service.
234
+ """
235
+ self._client = self.get_client()
236
+ self._graph = self.get_graph_client(self._client)
237
+ self.user = self.credentials.get('user', self.user)
238
+ self.password = self.credentials.get('password', self.password)
239
+ if self.user and self.password:
240
+ self.user_credentials = self.user_auth(
241
+ username=self.user,
242
+ password=self.password,
243
+ scopes=self.scopes
244
+ )
245
+ return self
246
+
247
+ async def get_msteams_channel_messages(
248
+ self,
249
+ team_id: str,
250
+ channel_id: str,
251
+ start_time: Optional[str] = None,
252
+ end_time: Optional[str] = None,
253
+ max_messages: Optional[int] = None
254
+ ) -> List[Dict]:
255
+ """
256
+ Fetches messages from a Teams channel.
257
+
258
+ Args:
259
+ team_id (str): The ID of the team.
260
+ channel_id (str): The ID of the channel.
261
+ start_time (str, optional): ISO 8601 formatted start time to filter messages.
262
+ end_time (str, optional): ISO 8601 formatted end time to filter messages.
263
+ max_messages (int, optional): Maximum number of messages to retrieve.
264
+
265
+ Returns:
266
+ List[Dict]: A list of message objects.
267
+ """
268
+ if not self._graph:
269
+ raise ComponentError(
270
+ "Graph client not initialized. Please call 'open' first."
271
+ )
272
+
273
+ messages = []
274
+ print('Credentials <>', self.credentials)
275
+ _filter = f"lastModifiedDateTime gt {start_time!s} and lastModifiedDateTime lt {end_time!s}"
276
+ print('Filter > ', _filter)
277
+ try:
278
+ query_params = GetAllMessagesRequestBuilder.GetAllMessagesRequestBuilderGetQueryParameters(
279
+ filter=_filter
280
+ )
281
+
282
+ request_configuration = RequestConfiguration(
283
+ query_parameters=query_params,
284
+ )
285
+
286
+ messages = await self._graph.teams.by_team_id(team_id).channels.get_all_messages.get(
287
+ request_configuration=request_configuration
288
+ )
289
+
290
+ print('Messages ', messages)
291
+ return messages
292
+ except Exception as e:
293
+ self._logger.error(
294
+ f"Failed to retrieve channel messages: {e}"
295
+ )
296
+ raise ComponentError(
297
+ f"Could not retrieve channel messages: {e}"
298
+ )
299
+
300
+ def _is_within_time_range(
301
+ self,
302
+ message_time_str: str,
303
+ start_time: Optional[str],
304
+ end_time: Optional[str]
305
+ ) -> bool:
306
+ """
307
+ Checks if a message's time is within the specified time range.
308
+
309
+ Args:
310
+ message_time_str (str): The message's creation time as an ISO 8601 string.
311
+ start_time (str, optional): ISO 8601 formatted start time.
312
+ end_time (str, optional): ISO 8601 formatted end time.
313
+
314
+ Returns:
315
+ bool: True if within range, False otherwise.
316
+ """
317
+ message_time = datetime.fromisoformat(message_time_str.rstrip('Z'))
318
+
319
+ if start_time:
320
+ start = datetime.fromisoformat(start_time.rstrip('Z'))
321
+ if message_time < start:
322
+ return False
323
+
324
+ if end_time:
325
+ end = datetime.fromisoformat(end_time.rstrip('Z'))
326
+ if message_time > end:
327
+ return False
328
+
329
+ return True
330
+
331
+ async def get_channel_details(self, team_id: str, channel_id: str) -> Dict:
332
+ """
333
+ Fetches details of a Teams channel.
334
+
335
+ Args:
336
+ team_id (str): The ID of the team.
337
+ channel_id (str): The ID of the channel.
338
+
339
+ Returns:
340
+ Dict: A dictionary containing channel details.
341
+ """
342
+ if not self._graph:
343
+ raise ComponentError(
344
+ "Graph client not initialized. Please call 'open' first."
345
+ )
346
+
347
+ try:
348
+ channel_details = await self._graph.teams.by_team_id(team_id).channels.by_channel_id(channel_id).get()
349
+
350
+ print('CHANNEL DETAILS > ', channel_details)
351
+ self._logger.info(
352
+ f"Retrieved details for channel: {channel_details.get('displayName')}"
353
+ )
354
+ return channel_details
355
+ except Exception as e:
356
+ self._logger.error(
357
+ f"Failed to retrieve channel details: {e}"
358
+ )
359
+ raise ComponentError(
360
+ f"Could not retrieve channel details: {e}"
361
+ )
362
+
363
+ async def get_channel_members(self, team_id: str, channel_id: str) -> List[Dict]:
364
+ """
365
+ Fetches the list of members in a Teams channel.
366
+
367
+ Args:
368
+ team_id (str): The ID of the team.
369
+ channel_id (str): The ID of the channel.
370
+
371
+ Returns:
372
+ List[Dict]: A list of member objects.
373
+ """
374
+ if not self._graph:
375
+ raise ComponentError(
376
+ "Graph client not initialized. Please call 'open' first."
377
+ )
378
+
379
+ members = []
380
+ endpoint = self._graph.teams[team_id].channels[channel_id].members
381
+ query_params = {
382
+ '$top': 50 # Adjust as needed
383
+ }
384
+
385
+ # Initial request
386
+ request = endpoint.get(
387
+ query_parameters=query_params
388
+ )
389
+
390
+ try:
391
+ # Pagination loop
392
+ while request:
393
+ response = await self._graph.send_request(request)
394
+ response_data = await response.json()
395
+
396
+ batch_members = response_data.get('value', [])
397
+ members.extend(batch_members)
398
+
399
+ # Check for pagination
400
+ next_link = response_data.get('@odata.nextLink')
401
+ if next_link:
402
+ # Create a new request for the next page
403
+ request = self._graph.create_request("GET", next_link)
404
+ else:
405
+ break
406
+
407
+ self._logger.info(
408
+ f"Retrieved {len(members)} members from channel."
409
+ )
410
+ return members
411
+ except Exception as e:
412
+ self._logger.error(
413
+ f"Failed to retrieve channel members: {e}"
414
+ )
415
+ raise ComponentError(f"Could not retrieve channel members: {e}")
416
+
417
+ async def find_channel_by_name(self, channel_name: str):
418
+ if not self._graph:
419
+
420
+ raise ComponentError(
421
+ "Graph client not initialized. Please call 'open' first."
422
+ )
423
+
424
+ # List all teams
425
+ teams = await self._graph.teams.get()
426
+ print(f"Total Teams Found: {len(teams)}")
427
+
428
+ for team in teams:
429
+ team_id = team.get('id')
430
+ team_display_name = team.get(
431
+ 'displayName',
432
+ 'Unknown Team'
433
+ )
434
+ print(f"Checking Team: {team_display_name} (ID: {team_id})")
435
+
436
+ # List channels in the team
437
+ channels = await self._graph.list_channels_in_team(team_id)
438
+ print(
439
+ f"Total Channels in Team '{team_display_name}': {len(channels)}"
440
+ )
441
+
442
+ # Search for the channel by name
443
+ for channel in channels:
444
+ channel_display_name = channel.get('displayName', '')
445
+ if channel_display_name.lower() == channel_name.lower():
446
+ channel_id = channel.get('id')
447
+ print(
448
+ f"Channel Found: {channel_display_name}"
449
+ )
450
+ print(
451
+ f"Team ID: {team_id}"
452
+ )
453
+ print(
454
+ f"Channel ID: {channel_id}"
455
+ )
456
+
457
+ # return team_id and channel_id
458
+ return team_id, channel_id
459
+
460
+ async def list_chats(self, user: str) -> List[Dict]:
461
+ """
462
+ Lists all chats accessible to the application or user.
463
+
464
+ Returns:
465
+ List[Dict]: A list of chat objects.
466
+ """
467
+ if not self._graph:
468
+ raise ComponentError(
469
+ "Graph client not initialized. Please call 'open' first."
470
+ )
471
+
472
+ try:
473
+ chats = []
474
+ chats = await self._graph.users.by_user_id(
475
+ user
476
+ ).chats.get()
477
+
478
+ # getting chats from ChatCollectionResponse:
479
+ return chats.value
480
+
481
+ except Exception as e:
482
+ self._logger.error(f"Failed to retrieve chats: {e}")
483
+ raise ComponentError(f"Could not retrieve chats: {e}")
484
+
485
+ async def list_user_chats(self, user: str) -> List[Dict]:
486
+ """
487
+ Lists all chats accessible to the User.
488
+
489
+ Returns:
490
+ List[Dict]: A list of chat objects.
491
+ """
492
+ if not self._graph:
493
+ raise ComponentError(
494
+ "Graph client not initialized. Please call 'open' first."
495
+ )
496
+
497
+ try:
498
+ chats = []
499
+ chats = await self._graph.users.by_user_id(
500
+ user
501
+ ).chats.get()
502
+
503
+ # getting chats from ChatCollectionResponse:
504
+ return chats.value
505
+
506
+ except Exception as e:
507
+ self._logger.error(
508
+ f"Failed to retrieve chats: {e}"
509
+ )
510
+ raise ComponentError(
511
+ f"Could not retrieve chats: {e}"
512
+ )
513
+
514
+ async def find_chat_by_name(self, chat_name: str, user: str = None) -> Optional[str]:
515
+ """
516
+ Finds a chat by its name (topic) and returns its chat_id.
517
+
518
+ Args:
519
+ chat_name (str): The name of the chat to find.
520
+
521
+ Returns:
522
+ Optional[str]: The chat_id if found, else None.
523
+ """
524
+ chats = await self.list_chats(user or DEFAULT_TEAMS_USER)
525
+ for chat in chats:
526
+ if chat.chat_type.Group == 'group' and chat.topic == chat_name:
527
+ return chat
528
+ return None
529
+
530
+ async def get_chat_messages(
531
+ self,
532
+ chat_id: str,
533
+ start_time: Optional[str] = None,
534
+ end_time: Optional[str] = None,
535
+ messages_per_page: int = 50,
536
+ max_messages: Optional[int] = None
537
+ ) -> Optional[List]:
538
+ """
539
+ Get chat messages.
540
+
541
+ Args:
542
+ chat_id (str): Id of Chat
543
+
544
+ Returns:
545
+ Optional[List]: All Chat Messages based on criteria.
546
+ """
547
+ args = {
548
+ "orderby": ["lastModifiedDateTime desc"]
549
+ }
550
+ args['top'] = min(messages_per_page, 50) # max 50 message per-page
551
+ if start_time and end_time:
552
+ args['filter'] = f"lastModifiedDateTime gt {start_time!s} and lastModifiedDateTime lt {end_time!s}"
553
+
554
+ query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
555
+ **args
556
+ )
557
+ request_configuration = RequestConfiguration(
558
+ query_parameters=query_params,
559
+ )
560
+
561
+ messages = []
562
+ response = await self._graph.chats.by_chat_id(chat_id).messages.get(
563
+ request_configuration=request_configuration
564
+ )
565
+
566
+ if isinstance(response, ChatMessageCollectionResponse):
567
+ messages.extend(response.value)
568
+ else:
569
+ self._logger.warning(
570
+ f"Unable to find Chat messages over {chat_id}, {response}"
571
+ )
572
+ return []
573
+
574
+ try:
575
+ next_link = response.odata_next_link
576
+
577
+ while next_link:
578
+ response = await self._graph.chats.with_url(next_link).get()
579
+ if not response:
580
+ break
581
+
582
+ # for user in users.value:
583
+ messages.extend(response.value)
584
+
585
+ # Check if we have reached the max_users limit
586
+ if max_messages and len(messages) >= max_messages:
587
+ # Trim the list to the max_users limit
588
+ messages = messages[:max_messages]
589
+ break
590
+
591
+ next_link = response.odata_next_link
592
+ except Exception as exc:
593
+ raise ComponentError(
594
+ f"Could not retrieve chat messages: {exc}"
595
+ )
596
+ # returning the messages
597
+ return messages
598
+
599
+ async def get_all_items(self, client, initial_request):
600
+ items = []
601
+
602
+ # Perform the initial request
603
+ response = await initial_request()
604
+
605
+ # Add initial response items
606
+ if hasattr(response, 'value'):
607
+ items.extend(response.value)
608
+
609
+ # Check for next link and paginate
610
+ while hasattr(response, 'odata_next_link') and response.odata_next_link:
611
+ # Use the next link to get the next set of results
612
+ next_request = client.request_adapter.send_async(
613
+ response.get_next_page_request_information(),
614
+ response_type=type(response)
615
+ )
616
+ response = await next_request
617
+
618
+ if hasattr(response, 'value'):
619
+ items.extend(response.value)
620
+
621
+ return items
622
+
623
+ async def get_user_photo(self, user_id: str):
624
+ try:
625
+ photo = await self._graph.users.by_user_id(user_id).photo.content.get()
626
+ return photo # This returns the photo content as a binary stream
627
+ except Exception as e:
628
+ if "ImageNotFoundException" in str(e) or "404" in str(e):
629
+ # Return None or an alternative to indicate no photo found
630
+ return None
631
+ self._logger.error(
632
+ f"Failed to retrieve photo for user {user_id}: {e}"
633
+ )
634
+ return None
635
+
636
+ async def list_users(
637
+ self,
638
+ fields: list = [
639
+ "id",
640
+ "displayName",
641
+ "surname",
642
+ "givenName",
643
+ "mail",
644
+ "department",
645
+ "jobTitle",
646
+ "officeLocation",
647
+ "mobilePhone",
648
+ "userPrincipalName",
649
+ "createdDateTime"
650
+ ],
651
+ users_per_page: int = 50,
652
+ max_users: int = None,
653
+ with_photo: bool = True,
654
+ order_by: str = "displayName",
655
+ sort_order: str = "asc"
656
+ ):
657
+ args = {
658
+ "select": fields,
659
+ "top": min(users_per_page, 50), # Limit to 50 users per page
660
+ "orderby": f"{order_by} {sort_order}"
661
+ }
662
+ # Define the initial request configuration (select specific fields if needed)
663
+ query_params = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
664
+ **args
665
+ )
666
+ request_config = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
667
+ query_parameters=query_params
668
+ )
669
+
670
+ users_list = []
671
+
672
+ users = await self._graph.users.get(request_configuration=request_config)
673
+ # for user in users.value:
674
+ users_list.extend(users.value)
675
+
676
+ next_link = users.odata_next_link
677
+ while next_link:
678
+ users = await self._graph.users.with_url(next_link).get()
679
+ if not users:
680
+ break
681
+
682
+ # for user in users.value:
683
+ users_list.extend(users.value)
684
+
685
+ # Check if we have reached the max_users limit
686
+ if max_users and len(users_list) >= max_users:
687
+ # Trim the list to the max_users limit
688
+ users_list = users_list[:max_users]
689
+ break
690
+
691
+ next_link = users.odata_next_link
692
+
693
+ if with_photo is True:
694
+ for user in users_list:
695
+ user_photo = await self.get_user_photo(user.id)
696
+ if user_photo:
697
+ user.photo = user_photo
698
+ else:
699
+ user.photo = "No photo available"
700
+
701
+ # Sort the users locally by createdDateTime in ascending order
702
+ if len(users_list) > 0 and getattr(users_list[0], 'created_date_time', None) is not None:
703
+ users_list.sort(key=lambda user: user.created_date_time)
704
+ return users_list
705
+
706
+ async def get_user(self, user_id: str) -> Dict:
707
+ """
708
+ Fetches user information from Microsoft Graph API based on user ID.
709
+
710
+ Args:
711
+ user_id (str): The Azure AD object ID of the user.
712
+
713
+ Returns:
714
+ Dict: User information as a dictionary.
715
+ """
716
+ if not self._graph:
717
+ raise ComponentError(
718
+ "Graph client not initialized. Please call 'open' first."
719
+ )
720
+
721
+ try:
722
+ # Use the email (userPrincipalName) to get the user
723
+ return await self._graph.users.by_user_id(user_id).get()
724
+ except Exception as e:
725
+ if "Insufficient privileges" in str(e):
726
+ self._logger.error(
727
+ "Please ensure your app has User.Read.All or Directory.Read.All permissions."
728
+ )
729
+ else:
730
+ self._logger.error(
731
+ f"Failed to retrieve user with email {user_id}: {e}"
732
+ )
733
+ raise ComponentError(
734
+ f"Could not retrieve user info: {e}"
735
+ )
736
+
737
+ def _filter_messages_by_user(self, messages: list, user: object):
738
+ filtered_messages = []
739
+ user_id = user.id
740
+ for message in messages:
741
+ if isinstance(message, ChatMessage):
742
+ if (message.from_ and message.from_.user and message.from_.user.id == user_id) or message.reply_to_id:
743
+ filtered_messages.append(message)
744
+ return filtered_messages
745
+
746
+ ## MS Teams Chats and messages:
747
+ async def user_chat_messages(
748
+ self,
749
+ user: object,
750
+ chat_id: str,
751
+ start_time: Optional[str] = None,
752
+ end_time: Optional[str] = None,
753
+ messages_per_page: int = 50,
754
+ max_messages: Optional[int] = None,
755
+ max_retries: int = 3
756
+ ) -> Optional[List]:
757
+ """
758
+ Get User chat messages.
759
+
760
+ Args:
761
+ chat_id (str): Id of Chat
762
+
763
+ Returns:
764
+ Optional[List]: All Chat Messages based on criteria.
765
+ """
766
+ args = {
767
+ "orderby": ["lastModifiedDateTime desc"]
768
+ }
769
+ args['top'] = min(messages_per_page, 50) # max 50 message per-page
770
+ if start_time and end_time:
771
+ if isinstance(start_time, datetime):
772
+ start_time = start_time.isoformat() + 'Z'
773
+ if isinstance(end_time, datetime):
774
+ end_time = end_time.isoformat() + 'Z'
775
+ args['filter'] = f"lastModifiedDateTime gt {start_time!s} and lastModifiedDateTime lt {end_time!s}"
776
+ else:
777
+ start_time = (datetime.utcnow() - timedelta(days=1)).isoformat() + 'Z'
778
+ end_time = datetime.utcnow().isoformat() + 'Z'
779
+ args['filter'] = f"lastModifiedDateTime gt {start_time} and lastModifiedDateTime lt {end_time}"
780
+
781
+ query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
782
+ **args
783
+ )
784
+ request_configuration = RequestConfiguration(
785
+ query_parameters=query_params,
786
+ )
787
+
788
+ messages = []
789
+ response = await self._graph.chats.by_chat_id(chat_id).messages.get(
790
+ request_configuration=request_configuration
791
+ )
792
+
793
+ if isinstance(response, ChatMessageCollectionResponse):
794
+ messages.extend(response.value)
795
+ else:
796
+ self._logger.warning(
797
+ f"Unable to find Chat messages over {chat_id}, {response}"
798
+ )
799
+ return []
800
+
801
+ # Filter messages based on the user's `id`
802
+ messages = self._filter_messages_by_user(messages, user)
803
+ next_link = response.odata_next_link
804
+
805
+ for attempt in range(max_retries):
806
+ try:
807
+ # retry for don't loose API calls
808
+ while next_link:
809
+ response = await self._graph.chats.with_url(next_link).get()
810
+ if not response:
811
+ break
812
+
813
+ # Check if we have reached the max_users limit
814
+ if max_messages and len(messages) >= max_messages:
815
+ # Trim the list to the max_users limit
816
+ messages = messages[:max_messages]
817
+ break
818
+
819
+ # for user in users.value:
820
+ filtered = self._filter_messages_by_user(response.value, user)
821
+ messages.extend(filtered)
822
+
823
+ next_link = response.odata_next_link
824
+ except ODataError as exc:
825
+ if exc.error.code == "TooManyRequests":
826
+ retry_after = exc.error.inner_error.additional_data.get('Retry-After', None)
827
+ if retry_after:
828
+ wait_time = int(retry_after)
829
+ print(f"Rate limit hit. Retrying in {wait_time} seconds...")
830
+ await asyncio.sleep(wait_time)
831
+ else:
832
+ print("Rate limit hit. Retrying with exponential backoff...")
833
+ await asyncio.sleep(2 ** attempt)
834
+ except Exception as exc:
835
+ raise ComponentError(
836
+ f"Could not retrieve chat messages: {exc}"
837
+ )
838
+ # returning the messages
839
+ return messages