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,220 @@
1
+ from abc import abstractmethod
2
+ from typing import Any
3
+ from collections.abc import Callable
4
+ import asyncio
5
+ from concurrent.futures import ThreadPoolExecutor
6
+ import msal
7
+ from office365.graph_client import GraphClient
8
+ from office365.sharepoint.client_context import ClientContext
9
+ from office365.runtime.auth.user_credential import UserCredential
10
+ from office365.runtime.auth.client_credential import ClientCredential
11
+ from navconfig.logging import logging
12
+ from ..conf import (
13
+ SHAREPOINT_TENANT_NAME,
14
+ O365_CLIENT_ID,
15
+ O365_CLIENT_SECRET,
16
+ O365_TENANT_ID,
17
+ )
18
+ from .credentials import CredentialsInterface
19
+
20
+
21
+ logging.getLogger('msal').setLevel(logging.INFO)
22
+
23
+
24
+ class O365Client(CredentialsInterface):
25
+ """
26
+ O365Client
27
+
28
+ Overview
29
+
30
+ The O365Client class is an abstract base class for managing connections to Office 365 services.
31
+ It handles authentication, credential processing, and provides a method for obtaining the
32
+ Office 365 context. It uses the Office 365 Python SDK for authentication and context management.
33
+
34
+ .. table:: Properties
35
+ :widths: auto
36
+
37
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
38
+ | Name | Required | Description |
39
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
40
+ | url | No | The base URL for the Office 365 service. |
41
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
42
+ | tenant | Yes | The tenant ID for the Office 365 service. |
43
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
44
+ | site | No | The site URL for the Office 365 service. |
45
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
46
+ | auth_context | Yes | The authentication context for Office 365. |
47
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
48
+ | context | Yes | The context object for Office 365 operations. |
49
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
50
+ | credentials | Yes | A dictionary containing the credentials for authentication. |
51
+ +------------------+----------+--------------------------------------------------------------------------------------------------+
52
+
53
+ Return
54
+
55
+ The methods in this class manage the authentication and connection setup for Office 365 services,
56
+ providing an abstract base for subclasses to implement specific service interactions.
57
+
58
+ """ # noqa
59
+ _credentials: dict = {
60
+ "username": str,
61
+ "password": str,
62
+ "client_id": str,
63
+ "client_secret": str,
64
+ "tenant": str,
65
+ "site": str,
66
+ }
67
+
68
+ def __init__(self, *args, **kwargs) -> None:
69
+ self.url: str = None
70
+ self.tenant_id: str = None
71
+ self.auth_context: Any = None
72
+ self.context: Any = None
73
+ self._access_token: str = None
74
+ self._graph_client: Callable = None
75
+ self._logger = logging.getLogger(__name__)
76
+ self._executor = ThreadPoolExecutor()
77
+ # Default credentials
78
+ self._default_tenant_id = O365_TENANT_ID
79
+ self._default_client_id = O365_CLIENT_ID
80
+ self._default_client_secret = O365_CLIENT_SECRET
81
+ self._default_tenant_name = SHAREPOINT_TENANT_NAME
82
+ super(O365Client, self).__init__(*args, **kwargs)
83
+
84
+ @abstractmethod
85
+ def get_context(self, url: str, *args):
86
+ pass
87
+
88
+ @abstractmethod
89
+ async def _start_(self, **kwargs):
90
+ pass
91
+
92
+ async def run_in_executor(self, fn, *args, **kwargs):
93
+ """
94
+ Calling any blocking process in an executor.
95
+ """
96
+ return await asyncio.get_event_loop().run_in_executor(
97
+ self._executor, fn, *args, **kwargs
98
+ )
99
+
100
+ def processing_credentials(self):
101
+ super().processing_credentials()
102
+ ## getting Tenant and Site from credentials:
103
+ try:
104
+ self.tenant = self.credentials.get('tenant', None)
105
+ if not self.tenant:
106
+ self.tenant = self._default_tenant_name
107
+ self.site = self.credentials.get('site', None)
108
+ except KeyError as e:
109
+ raise RuntimeError(
110
+ f"Office365: Missing Tenant or Site Configuration: {e}."
111
+ ) from e
112
+
113
+ def connection(self):
114
+ # calling before run:
115
+ self._start_()
116
+ # Processing The Credentials:
117
+ if hasattr(self, "credentials"):
118
+ username = self.credentials.get("username")
119
+ password = self.credentials.get("password")
120
+ client_id = self.credentials.get("client_id")
121
+ if not client_id:
122
+ client_id = self._default_client_id
123
+ client_secret = self.credentials.get("client_secret")
124
+ if not client_secret:
125
+ client_secret = self._default_client_secret
126
+ else:
127
+ # Maybe getting from default credentials?
128
+ client_id = getattr(self, 'client_id', self._default_client_id)
129
+ client_secret = getattr(self, 'client_secret', self._default_client_secret)
130
+ if not client_id or client_secret:
131
+ logging.error(
132
+ "Office365: Wrong Credentials or missing Credentials")
133
+ raise RuntimeError(
134
+ "Office365: Wrong Credentials or missing Credentials"
135
+ )
136
+ try:
137
+ if username is not None:
138
+ self.context = ClientContext(self.url).with_credentials(
139
+ UserCredential(username, password)
140
+ )
141
+ try:
142
+ token = self.user_auth(username, password)
143
+ self._access_token = token.get('access_token')
144
+ except Exception as err:
145
+ self._logger.warning(
146
+ f"Office365: Authentication Error: {err}"
147
+ )
148
+ else:
149
+ self.auth_context = ClientCredential(client_id, client_secret)
150
+ self.context = self.get_context(self.url).with_credentials(
151
+ self.auth_context
152
+ )
153
+ token = self.acquire_token()
154
+ self._access_token = token.get('access_token')
155
+ # Create Graph client
156
+ self._graph_client = GraphClient(
157
+ acquire_token_callback=lambda: self.access_token
158
+ )
159
+ logging.debug("Office365: Authentication success")
160
+ except Exception as err:
161
+ logging.error(f"Office365: Authentication Error: {err}")
162
+ raise RuntimeError(f"Office365: Authentication Error: {err}") from err
163
+ return self
164
+
165
+ def user_auth(self, username: str, password: str, scopes: list = None) -> dict:
166
+ tenant_id = self.credentials.get('tenant_id', self._default_tenant_id)
167
+ authority_url = f'https://login.microsoftonline.com/{tenant_id}'
168
+ client_id = self.credentials.get("client_id")
169
+ if not client_id:
170
+ client_id = self._default_client_id
171
+
172
+ if not scopes:
173
+ scopes = ["https://graph.microsoft.com/.default"]
174
+ app = msal.PublicClientApplication(
175
+ authority=authority_url,
176
+ client_id=client_id,
177
+ client_credential=None
178
+ )
179
+ result = app.acquire_token_by_username_password(
180
+ username,
181
+ password,
182
+ scopes=scopes
183
+ )
184
+ if "access_token" not in result:
185
+ error_message = result.get('error_description', 'Unknown error')
186
+ error_code = result.get('error', 'Unknown error code')
187
+ raise RuntimeError(
188
+ f"Failed to obtain access token: {error_code} - {error_message}"
189
+ )
190
+ return result
191
+
192
+ def acquire_token(self, scopes: list = None) -> dict:
193
+ """
194
+ Acquire a Token via MSAL.
195
+ """
196
+ client_id = self.credentials.get("client_id")
197
+ if not client_id:
198
+ client_id = self._default_client_id
199
+ client_secret = self.credentials.get("client_secret")
200
+ if not client_secret:
201
+ client_secret = self._default_client_secret
202
+ tenant_id = self.credentials.get('tenant_id', self._default_tenant_id)
203
+ if not scopes:
204
+ scopes = ["https://graph.microsoft.com/.default"]
205
+ authority_url = f'https://login.microsoftonline.com/{tenant_id}'
206
+ app = msal.ConfidentialClientApplication(
207
+ authority=authority_url,
208
+ client_id=client_id,
209
+ client_credential=client_secret
210
+ )
211
+ result = app.acquire_token_for_client(
212
+ scopes=scopes
213
+ )
214
+ if "access_token" not in result:
215
+ error_message = result.get('error_description', 'Unknown error')
216
+ error_code = result.get('error', 'Unknown error code')
217
+ raise RuntimeError(
218
+ f"Failed to obtain access token: {error_code} - {error_message}"
219
+ )
220
+ return result
@@ -0,0 +1,284 @@
1
+ import os
2
+ from typing import List
3
+ from collections.abc import Callable
4
+ from pathlib import Path
5
+ import pandas as pd
6
+ from io import BytesIO
7
+ from office365.graph_client import GraphClient
8
+ from office365.onedrive.driveitems.driveItem import DriveItem
9
+ from office365.onedrive.drives.drive import Drive
10
+ from ..exceptions import FileError, FileNotFound
11
+ from .O365Client import O365Client
12
+
13
+
14
+ class OneDriveClient(O365Client):
15
+ """
16
+ OneDrive Client.
17
+
18
+ Interface for Managing connections to OneDrive resources.
19
+
20
+ Methods:
21
+ file_list: Lists files in a specified OneDrive folder.
22
+ file_search: Searches for files matching a query.
23
+ file_download: Downloads a single file by its item ID.
24
+ download_files: Downloads multiple files provided as a list of dictionaries containing file info.
25
+ folder_download: Downloads a folder and its contents recursively.
26
+ file_delete: Deletes a file or folder by its item ID.
27
+ upload_files: Uploads multiple files to a specified OneDrive folder.
28
+ upload_file: Uploads a single file to OneDrive.
29
+ upload_folder: Uploads a local folder and its contents to OneDrive recursively.
30
+
31
+ """
32
+
33
+ def get_context(self, url: str, *args) -> Callable:
34
+ # For OneDrive, we primarily use GraphClient
35
+ if not self._graph_client:
36
+ self._graph_client = GraphClient(acquire_token=lambda: self.access_token)
37
+ return self._graph_client
38
+
39
+ def _start_(self, **kwargs):
40
+ return True
41
+
42
+ async def download_excel_file(
43
+ self,
44
+ item_id: str,
45
+ destination: Path = None,
46
+ as_pandas: bool = False
47
+ ):
48
+ """
49
+ Download an Excel file from OneDrive by item ID.
50
+ If `as_pandas` is True, return as a pandas DataFrame.
51
+ If `as_pandas` is False, save to the destination path.
52
+ """
53
+ try:
54
+ drive = self._graph_client.me.drive
55
+ file_item = drive.items[item_id]
56
+
57
+ if as_pandas:
58
+ bytes_buffer = BytesIO()
59
+ file_item.download(bytes_buffer).execute_query()
60
+ bytes_buffer.seek(0)
61
+ df = pd.read_excel(bytes_buffer)
62
+ return df
63
+ else:
64
+ if not destination:
65
+ raise ValueError("Destination path must be provided when `as_pandas` is False.")
66
+ with open(destination, "wb") as local_file:
67
+ file_item.download(local_file).execute_query()
68
+ return str(destination)
69
+ except Exception as err:
70
+ self._logger.error(f"Error downloading Excel file {item_id}: {err}")
71
+ raise FileError(f"Error downloading Excel file {item_id}: {err}") from err
72
+
73
+ async def upload_dataframe_as_excel(
74
+ self,
75
+ df: pd.DataFrame,
76
+ file_name: str,
77
+ destination_folder: str = None
78
+ ):
79
+ """
80
+ Upload a pandas DataFrame as an Excel file to OneDrive.
81
+ """
82
+ try:
83
+ output = BytesIO()
84
+ df.to_excel(output, index=False)
85
+ output.seek(0)
86
+ drive = self._graph_client.me.drive
87
+ if destination_folder:
88
+ item_path = f"{destination_folder}/{file_name}"
89
+ else:
90
+ item_path = file_name
91
+ target_item = drive.root.item_with_path(item_path)
92
+ target_item.upload(output).execute_query()
93
+ return {
94
+ "name": file_name,
95
+ "webUrl": target_item.web_url
96
+ }
97
+ except Exception as err:
98
+ self._logger.error(f"Error uploading DataFrame as Excel file {file_name}: {err}")
99
+ raise FileError(f"Error uploading DataFrame as Excel file {file_name}: {err}") from err
100
+
101
+ async def file_list(self, folder_path: str = None) -> List[dict]:
102
+ """
103
+ List files in a given OneDrive folder.
104
+ """
105
+ try:
106
+ drive = self._graph_client.me.drive
107
+ if folder_path:
108
+ folder_item = drive.root.get_by_path(folder_path)
109
+ else:
110
+ folder_item = drive.root
111
+
112
+ items = folder_item.children.get().execute_query()
113
+ file_list = []
114
+ for item in items:
115
+ file_info = {
116
+ "name": item.name,
117
+ "id": item.id,
118
+ "webUrl": item.web_url,
119
+ "isFolder": item.folder is not None
120
+ }
121
+ file_list.append(file_info)
122
+ return file_list
123
+ except Exception as err:
124
+ self._logger.error(f"Error listing files: {err}")
125
+ raise FileError(f"Error listing files: {err}") from err
126
+
127
+ async def file_search(self, search_query: str) -> List[dict]:
128
+ """
129
+ Search for files in OneDrive matching the search query.
130
+ """
131
+ try:
132
+ drive = self._graph_client.me.drive
133
+ items = drive.root.search(search_query).get().execute_query()
134
+ search_results = []
135
+ for item in items:
136
+ file_info = {
137
+ "name": item.name,
138
+ "id": item.id,
139
+ "webUrl": item.web_url,
140
+ "path": item.parent_reference.path,
141
+ "isFolder": item.folder is not None
142
+ }
143
+ search_results.append(file_info)
144
+ return search_results
145
+ except Exception as err:
146
+ self._logger.error(f"Error searching files: {err}")
147
+ raise FileError(f"Error searching files: {err}") from err
148
+
149
+ async def file_download(self, item_id: str, destination: Path):
150
+ """
151
+ Download a file from OneDrive by item ID.
152
+ """
153
+ try:
154
+ drive = self._graph_client.me.drive
155
+ file_item = drive.items[item_id]
156
+ with open(destination, "wb") as local_file:
157
+ file_item.download(local_file).execute_query()
158
+ return str(destination)
159
+ except Exception as err:
160
+ self._logger.error(f"Error downloading file {item_id}: {err}")
161
+ raise FileError(f"Error downloading file {item_id}: {err}") from err
162
+
163
+ async def download_files(self, items: List[dict], destination_folder: Path):
164
+ """
165
+ Download multiple files from OneDrive.
166
+ """
167
+ downloaded_files = []
168
+ for item in items:
169
+ item_id = item.get("id")
170
+ file_name = item.get("name")
171
+ destination = destination_folder / file_name
172
+ await self.file_download(item_id, destination)
173
+ downloaded_files.append(str(destination))
174
+ return downloaded_files
175
+
176
+ async def folder_download(self, folder_id: str, destination_folder: Path):
177
+ """
178
+ Download a folder and its contents from OneDrive.
179
+ """
180
+ try:
181
+ drive = self._graph_client.me.drive
182
+ folder_item = drive.items[folder_id]
183
+ await self._download_folder_recursive(folder_item, destination_folder)
184
+ return True
185
+ except Exception as err:
186
+ self._logger.error(f"Error downloading folder {folder_id}: {err}")
187
+ raise FileError(f"Error downloading folder {folder_id}: {err}") from err
188
+
189
+ async def _download_folder_recursive(self, folder_item: DriveItem, local_path: Path):
190
+ """
191
+ Recursively download a folder's contents.
192
+ """
193
+ if not local_path.exists():
194
+ local_path.mkdir(parents=True)
195
+ items = folder_item.children.get().execute_query()
196
+ for item in items:
197
+ item_path = local_path / item.name
198
+ if item.folder:
199
+ await self._download_folder_recursive(item, item_path)
200
+ else:
201
+ await self.file_download(item.id, item_path)
202
+
203
+ async def file_delete(self, item_id: str):
204
+ """
205
+ Delete a file or folder in OneDrive by item ID.
206
+ """
207
+ try:
208
+ drive = self._graph_client.me.drive
209
+ item = drive.items[item_id]
210
+ item.delete_object().execute_query()
211
+ return True
212
+ except Exception as err:
213
+ self._logger.error(f"Error deleting item {item_id}: {err}")
214
+ raise FileError(f"Error deleting item {item_id}: {err}") from err
215
+
216
+ async def upload_files(self, files: List[Path], destination_folder: str = None):
217
+ """
218
+ Upload multiple files to OneDrive.
219
+ """
220
+ uploaded_files = []
221
+ for file_path in files:
222
+ file_name = file_path.name
223
+ uploaded_item = await self.upload_file(file_path, destination_folder)
224
+ uploaded_files.append(uploaded_item)
225
+ return uploaded_files
226
+
227
+ async def upload_file(self, file_path: Path, destination_folder: str = None):
228
+ """
229
+ Upload a single file to OneDrive.
230
+ """
231
+ try:
232
+ drive = self._graph_client.me.drive
233
+ if destination_folder:
234
+ target_folder = drive.root.get_by_path(destination_folder)
235
+ else:
236
+ target_folder = drive.root
237
+ with open(file_path, "rb") as content_file:
238
+ file_content = content_file.read()
239
+ uploaded_item = target_folder.children[file_path.name].upload(file_content).execute_query()
240
+ return {
241
+ "name": uploaded_item.name,
242
+ "id": uploaded_item.id,
243
+ "webUrl": uploaded_item.web_url
244
+ }
245
+ except Exception as err:
246
+ self._logger.error(f"Error uploading file {file_path}: {err}")
247
+ raise FileError(f"Error uploading file {file_path}: {err}") from err
248
+
249
+ async def upload_folder(self, local_folder: Path, destination_folder: str = None):
250
+ """
251
+ Upload a local folder and its contents to OneDrive.
252
+ """
253
+ uploaded_items = []
254
+ for root, dirs, files in os.walk(local_folder):
255
+ relative_path = Path(root).relative_to(local_folder)
256
+ one_drive_path = f"{destination_folder}/{relative_path}".strip("/") if destination_folder else str(relative_path) # noqa
257
+ # Create folder in OneDrive if it doesn't exist
258
+ await self._create_onedrive_folder(one_drive_path)
259
+ for file_name in files:
260
+ file_path = Path(root) / file_name
261
+ destination_path = f"{one_drive_path}/{file_name}".strip("/")
262
+ uploaded_item = await self.upload_file(file_path, destination_path)
263
+ uploaded_items.append(uploaded_item)
264
+ return uploaded_items
265
+
266
+ async def _create_onedrive_folder(self, folder_path: str):
267
+ """
268
+ Create a folder in OneDrive if it doesn't exist.
269
+ """
270
+ try:
271
+ drive = self._graph_client.me.drive
272
+ # Try to get the folder; if it doesn't exist, create it
273
+ folder_item = drive.root.get_by_path(folder_path)
274
+ folder_item.get().execute_query()
275
+ except Exception:
276
+ # Folder does not exist; create it
277
+ parent_path = "/".join(folder_path.split("/")[:-1])
278
+ folder_name = folder_path.split("/")[-1]
279
+ if parent_path:
280
+ parent_folder = drive.root.get_by_path(parent_path)
281
+ else:
282
+ parent_folder = drive.root
283
+ new_folder = parent_folder.children.add_folder(folder_name).execute_query()
284
+ self._logger.info(f"Created folder: {new_folder.web_url}")
@@ -0,0 +1,155 @@
1
+ import asyncio
2
+ from typing import List, Optional
3
+ from office365.graph_client import GraphClient
4
+ from office365.outlook.mail.attachments.attachment import Attachment
5
+ from office365.outlook.mail.folders.folder import MailFolder
6
+ from office365.outlook.user import OutlookUser
7
+ from pathlib import Path
8
+ from ..exceptions import FileError
9
+ from .O365Client import O365Client
10
+
11
+
12
+ class OutlookClient(O365Client):
13
+ """
14
+ Outlook Client.
15
+
16
+ Managing connections to Outlook Mail API.
17
+ """
18
+
19
+ def get_context(self, url: str = None, *args) -> GraphClient:
20
+ # For Outlook, we primarily use GraphClient
21
+ if not self._graph_client:
22
+ self._graph_client = GraphClient(acquire_token=lambda: self.access_token)
23
+ return self._graph_client
24
+
25
+ def _start_(self, **kwargs):
26
+ return True
27
+
28
+ async def list_messages(
29
+ self,
30
+ folder: str = "Inbox",
31
+ top: int = 10,
32
+ filter_query: str = None,
33
+ select_fields: List[str] = None,
34
+ ) -> List[dict]:
35
+ """
36
+ List messages in a specified folder.
37
+ """
38
+ try:
39
+ messages = []
40
+ mail_folder = self._graph_client.me.mail_folders[folder]
41
+ query = mail_folder.messages.top(top)
42
+ if filter_query:
43
+ query = query.filter(filter_query)
44
+ if select_fields:
45
+ query = query.select(select_fields)
46
+ msg_pages = query.get_paged()
47
+ while True:
48
+ for message in msg_pages:
49
+ messages.append({
50
+ "id": message.id,
51
+ "subject": message.subject,
52
+ "sender": message.sender.email_address.address,
53
+ "receivedDateTime": message.received_date_time,
54
+ })
55
+ if not msg_pages.has_next:
56
+ break
57
+ msg_pages = msg_pages.get_next()
58
+ return messages
59
+ except Exception as err:
60
+ self._logger.error(f"Error listing messages: {err}")
61
+ raise FileError(f"Error listing messages: {err}") from err
62
+
63
+ async def download_message(self, message_id: str, destination: Path):
64
+ """
65
+ Download a message by its ID.
66
+ """
67
+ try:
68
+ message = self._graph_client.me.messages[message_id].get().execute_query()
69
+ eml_content = message.mime_content
70
+ with open(destination, "wb") as file:
71
+ file.write(eml_content)
72
+ return str(destination)
73
+ except Exception as err:
74
+ self._logger.error(f"Error downloading message {message_id}: {err}")
75
+ raise FileError(f"Error downloading message {message_id}: {err}") from err
76
+
77
+ async def move_message(self, message_id: str, destination_folder_id: str):
78
+ """
79
+ Move a message to a different folder.
80
+ """
81
+ try:
82
+ message = self._graph_client.me.messages[message_id]
83
+ moved_message = message.move(destination_folder_id).execute_query()
84
+ return {
85
+ "id": moved_message.id,
86
+ "subject": moved_message.subject,
87
+ "folderId": destination_folder_id,
88
+ }
89
+ except Exception as err:
90
+ self._logger.error(f"Error moving message {message_id}: {err}")
91
+ raise FileError(f"Error moving message {message_id}: {err}") from err
92
+
93
+ async def search_messages(self, search_query: str, top: int = 10) -> List[dict]:
94
+ """
95
+ Search for messages matching the search query.
96
+ """
97
+ try:
98
+ messages = []
99
+ query = self._graph_client.me.messages.search(search_query).top(top)
100
+ msg_pages = query.get_paged()
101
+ while True:
102
+ for message in msg_pages:
103
+ messages.append({
104
+ "id": message.id,
105
+ "subject": message.subject,
106
+ "sender": message.sender.email_address.address,
107
+ "receivedDateTime": message.received_date_time,
108
+ })
109
+ if not msg_pages.has_next:
110
+ break
111
+ msg_pages = msg_pages.get_next()
112
+ return messages
113
+ except Exception as err:
114
+ self._logger.error(f"Error searching messages: {err}")
115
+ raise FileError(f"Error searching messages: {err}") from err
116
+
117
+ async def send_message(
118
+ self,
119
+ subject: str,
120
+ body: str,
121
+ to_recipients: List[str],
122
+ cc_recipients: Optional[List[str]] = None,
123
+ bcc_recipients: Optional[List[str]] = None,
124
+ attachments: Optional[List[Path]] = None,
125
+ from_address: Optional[str] = None,
126
+ ):
127
+ """
128
+ Send a message with optional attachments and optional 'on behalf of' another user.
129
+ """
130
+ try:
131
+ message = self._graph_client.me.messages.new()
132
+ message.subject = subject
133
+ message.body = {
134
+ "contentType": "HTML",
135
+ "content": body
136
+ }
137
+ message.to_recipients = [{"emailAddress": {"address": addr}} for addr in to_recipients]
138
+ if cc_recipients:
139
+ message.cc_recipients = [{"emailAddress": {"address": addr}} for addr in cc_recipients]
140
+ if bcc_recipients:
141
+ message.bcc_recipients = [{"emailAddress": {"address": addr}} for addr in bcc_recipients]
142
+ if attachments:
143
+ for attachment_path in attachments:
144
+ attachment = message.attachments.add_file_attachment(attachment_path.name)
145
+ with open(attachment_path, "rb") as file:
146
+ attachment.content_bytes = file.read()
147
+ if from_address:
148
+ # Set the 'from' address
149
+ message.from_ = {"emailAddress": {"address": from_address}}
150
+ # Send the message
151
+ message.send().execute_query()
152
+ return True
153
+ except Exception as err:
154
+ self._logger.error(f"Error sending message: {err}")
155
+ raise FileError(f"Error sending message: {err}") from err