diff --git a/.formatter.exs b/.formatter.exs index 39b9c0c..c0dbb59 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -3,6 +3,6 @@ inputs: [ "{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", - ".local/**/*.{ex,exs}" + "_local/**/*.{ex,exs}" ] ] diff --git a/.github/workflows/fly.yml b/.github/workflows/fly.yml deleted file mode 100644 index af529d4..0000000 --- a/.github/workflows/fly.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Fly Deploy -on: - push: - branches: - # - main - - wait_for_have_to_deploy #TODO: -jobs: - deploy: - name: Deploy app - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: superfly/flyctl-actions/setup-flyctl@master - - run: - flyctl secrets set TELEGRAM_BOT_TOKEN=${{ secrets.TELEGRAM_BOT_TOKEN }} AIER_API_TOKEN=${{ secrets.AIER_API_TOKEN }} --stage - # --stage Set secrets but skip deployment for machine apps - env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} - - run: flyctl deploy --remote-only - env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/.gitignore b/.gitignore index e021eee..c81d2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ erl_crash.dump *.ez # Ignore package tarball (built via "mix hex.build"). -aier_bot-*.tar +save_it-*.tar # Temporary files, for example, from tests. /tmp/ @@ -30,5 +30,5 @@ dev.sh start.sh run.sh nohup.out -.env -.local + +_local diff --git a/README.md b/README.md index 91d48fc..35fbeba 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,36 @@ -# AierBot +# save_it -A telegram bot to sync messages to https://aier.app +A telegram bot who save what you love in internet. -![save memo to AIer.app](./docs/assets/save-memo-to-aier.gif) +## supported services + +- [x] https://x.com/ +- [x] https://instagram.com/ +- [x] https://www.youtube.com/ +- [x] https://www.pinterest.com/ + +## Usage + +Just send the link to the bot. ## Build with - [Elixir](https://elixir-lang.org/) - [ex_gram](https://github.com/rockneurotiko/ex_gram) - -## Usage - -TODO: +- [cobalt api](https://github.com/imputnet/cobalt/blob/current/docs/api.md) ## Development ```sh +# install mix deps.get +``` +```sh +# run export TELEGRAM_BOT_TOKEN= -export AIER_API_TOKEN= -export OPENAI_API_KEY= +export GOOGLE_OAUTH_CLIENT_ID= +export GOOGLE_OAUTH_CLIENT_SECRET= -mix run --no-halt +iex -S mix run --no-halt ``` - -## Deployment - -### CI/CD - -GitHub Actions - -Secrets: - -- `TELEGRAM_BOT_TOKEN` -- `AIER_API_TOKEN` -- `FLY_API_TOKEN` diff --git a/config/runtime.exs b/config/runtime.exs index 352a73e..8b7d630 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -1,7 +1,6 @@ import Config -config :aier_bot, :telegram_bot_token, System.fetch_env!("TELEGRAM_BOT_TOKEN") -config :aier_bot, :aier_api_token, System.fetch_env!("AIER_API_TOKEN") -config :aier_bot, :openai_api_key, System.fetch_env!("OPENAI_API_KEY") - +config :save_it, :telegram_bot_token, System.fetch_env!("TELEGRAM_BOT_TOKEN") config :ex_gram, token: System.fetch_env!("TELEGRAM_BOT_TOKEN") +config :save_it, :google_oauth_client_id, System.fetch_env!("GOOGLE_OAUTH_CLIENT_ID") +config :save_it, :google_oauth_client_secret, System.fetch_env!("GOOGLE_OAUTH_CLIENT_SECRET") diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 0ce98ea..0000000 --- a/docs/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# fly - -- https://fly.io/apps/aier-bot/monitoring - -# docs - -- [Tesla.Adapter.Hackney](https://hexdocs.pm/tesla/Tesla.Adapter.Hackney.html) -- [Continuous Deployment with Fly.io and GitHub Actions](https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/) -- [System.get_env()](https://hexdocs.pm/elixir/System.html#get_env/0) -- [ex_gram](https://github.com/rockneurotiko/ex_gram#configuration) -- [Github Actions for Elixir CI](https://fly.io/phoenix-files/github-actions-for-elixir-ci/) diff --git a/docs/assets/save-memo-to-aier.gif b/docs/assets/save-memo-to-aier.gif deleted file mode 100644 index 76c4e22..0000000 Binary files a/docs/assets/save-memo-to-aier.gif and /dev/null differ diff --git a/docs/assets/savt_it_bot_logo.jpeg b/docs/assets/savt_it_bot_logo.jpeg deleted file mode 100644 index 0e861dc..0000000 Binary files a/docs/assets/savt_it_bot_logo.jpeg and /dev/null differ diff --git "a/docs/bug_log_\345\217\221\351\200\20120\345\210\206\351\222\237\351\225\277\350\247\206\351\242\221.png" "b/docs/bug_log_\345\217\221\351\200\20120\345\210\206\351\222\237\351\225\277\350\247\206\351\242\221.png" deleted file mode 100644 index 6a99ded..0000000 Binary files "a/docs/bug_log_\345\217\221\351\200\20120\345\210\206\351\222\237\351\225\277\350\247\206\351\242\221.png" and /dev/null differ diff --git a/docs/dev-log.md b/docs/dev-log.md deleted file mode 100644 index 108c8ee..0000000 --- a/docs/dev-log.md +++ /dev/null @@ -1,220 +0,0 @@ -# telegram channel & supergroup 管理权限 - -- supergroup: from.is_bot is true -- group: administrators includes from.id -- private: alway true - -```elixir -members: [ - %ExGram.Model.ChatMemberAdministrator{ - status: "administrator", - user: %{ - id: 7363668833, - username: "save_it_local_bot", - first_name: "Save it(local)", - is_bot: true - }, - can_be_edited: false, - is_anonymous: false, - can_manage_chat: true, - can_delete_messages: true, - can_manage_video_chats: true, - can_restrict_members: true, - can_promote_members: false, - can_change_info: true, - can_invite_users: true, - can_post_messages: nil, - can_edit_messages: nil, - can_pin_messages: true, - can_manage_topics: false, - custom_title: nil - }, - %ExGram.Model.ChatMemberOwner{ - status: "creator", - user: %{ - id: 940788576, - username: "ThaddeusJiang", - first_name: "TJ", - language_code: "zh-hans", - is_bot: false - }, - is_anonymous: true, - custom_title: nil - } -] -from: %{ - id: 1087968824, - username: "GroupAnonymousBot", - first_name: "Group", - is_bot: true -} -``` - -# 2024-09-05 - -# 2024-09-05 - -```sh -12:53:31.447 [warning] Failed to upload file to Google Drive, status: 404, body: %{"error" => %{"code" => 404, "errors" => [%{"domain" => "global", "location" => "fileId", "locationType" => "parameter", "message" => "File not found: 1ltD5i58EMpU-yni5y5KzGNzMm0rQH3H7.", "reason" => "notFound"}], "message" => "File not found: 1ltD5i58EMpU-yni5y5KzGNzMm0rQH3H7."}} -``` - -## Reason: - -我填错了 folder_id,因为我有两个 Google account。 - -所以,果然应该选择 folder_id 而不是输入。 - -- [ ] TODO: 开发 Google Drive API 允许用户选择自己的 folder - - [ ] list folders - - [ ] select folder and store - -# 2024-08-09 only bot owner can login and update folder - -```elixir -# 一般 -%{ - date: 1_723_185_293, - text: "", - from: %{ - id: 940_788_576, - username: "ThaddeusJiang", - first_name: "TJ", - language_code: "zh-hans", - is_bot: false - }, - chat: %{ - id: -4_255_771_899, - type: "group", - title: "Ren and My J", - all_members_are_administrators: true - }, - message_id: 734, - entities: [%{offset: 0, type: "bot_command", length: 24}] -} - -``` - -- 在一个包含 bot 的 group 随便发送 message - -```elixir -# owner -%{ - date: 1723105666, - text: "我只是试试", - from: %{ - id: 1087968824, - username: "GroupAnonymousBot", - first_name: "Group", - is_bot: true - }, - chat: %{id: -1002229528208, type: "supergroup", title: "📝个人记事本"}, - message_id: 19, - sender_chat: %{ - id: -1002229528208, - type: "supergroup", - title: "📝个人记事本" - } -} -``` - -在一个包含 bot 的 group,发送 command - -```elixir -%{ - date: 1_723_105_562, - text: "", - from: %{ - id: 1_087_968_824, - username: "GroupAnonymousBot", - first_name: "Group", - is_bot: true - }, - chat: %{id: -1_002_229_528_208, type: "supergroup", title: "📝个人记事本"}, - message_id: 16, - entities: [%{offset: 0, type: "bot_command", length: 24}], - sender_chat: %{ - id: -1_002_229_528_208, - type: "supergroup", - title: "📝个人记事本" - } -} -``` - -TIL: base64url RFC 4648 可以生成 URL and filename safe alphabet. - -https://x.com/kitayoshi_son/status/1815740828993962028 - -TIL: base64 可能包含 / + 等特殊字符,在作为文件路径时可能产生 bug。 - -- Base32 和 Base16 都不包含特殊字符 -- Base16(十六进制编码)在可读性和广泛使用方面具有优势,尤其是在软件开发和调试中。和 css color 类似 - -- [x] bug: [error] File.write failed, reason: enametoolong - -```elixir -hashed_url = :crypto.hash(:sha256, download_url) |> Base.url_encode64(padding: false) -``` - -- [x] bug: 21:38:27.786 [error] File.write failed, reason: enoent - -- [x] 2024-07-23 file path 不能含有太多特殊字符,例如斜杠 - -![error message: file path 不能含有太多特殊字符,例如斜杠](./error_log_file%20path%20不能含有太多特殊字符,例如斜杠.png) - -# 2024-07-22 logger 自带前缀,不需要额外加了 - -``` -21:42:39.299 [info] File written successfully -``` - -## FIXME: download youtube big video - -![error log: download youtube big video](./error_log_download_youtube_big_video.png) - -![log: stream date](./error_log_streaming_data.png) - -## FIXME: bug: 无法发送 20 分钟的长视频 - -![bug log: 无法发送 20 分钟的长视频](./bug_log_发送20分钟长视频.png) - -> Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future. -> Check the documentation of this method in https://core.telegram.org/bots/api#sendvideo - -## 2024-07-22 x.com 无法下载图片 tweet - -like: https://x.com/magnum_d1ngus/status/1815029025984643510?s=46 - -## ex_gram message struct - -![ex_gram message struct](ex_gram_input.png) - -# ins response - -```elixir -%{"audio" => false, "picker" => [%{"thumb" => "https://olly.imput.net/api/stream?id=5F-rJeRJV2IIH7_DU1KCq&exp=1721617274907&sig=hVWhMvTYTZbSIJwbW_UOql8zdEIMgKH2R_96baKjy7o&sec=yVOd07mry2m3ONZodwScrl7oCry7jABNv8pUsApc-N0&iv=8LL5l-FFqdeOJWfH3M0aXA", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452214393_889456523022480_3333917324961445363_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=nhiBpQU6K5AQ7kNvgFdgZrp&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYCi1798737LrMfL0qSPCRI9o3KYmG4D7idgrDI9CswbhQ&oe=66A3A295&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=T-MMmhNqazoO81A14IREn&exp=1721617274907&sig=G__OYm9A07VjNNbZzQP1AfD1g91wa54qDZDeHOMcwmU&sec=lCn3sVp4p-k1saXjivoKKto0xaGJmac8wEvziacYr_w&iv=bT4ciTQRqkY_nEjlooAMwg", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/451967523_3981212428779897_6069480984263987958_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=ArPt3FrmzOUQ7kNvgHaO_EU&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYDi93byaMp4kwKg4EzZDiwii21_NKILOLn0EBa4k4cbfQ&oe=66A38A12&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=NVNxeQMS7mZ4EE2yI4uvN&exp=1721617274907&sig=qcCyPPKzqa1haAlJCdlep0rLRwXQTvNb_dglA2zTvMM&sec=VbKAfrxhMMn_dXyMw8i5V50ULiA3hNtVFfFz1gHQ42c&iv=7Tv8zvUs041czaiBgYGY4Q", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452375076_908971944396934_2465836769974186877_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=1HzprAm1iwsQ7kNvgFlGIPQ&gid=9a607a73793d416f9360f9443700bf51&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYAaTBTpAIqYMGCKvdNQf9zEIMOS2dOqJZLz67fsxIHBRg&oe=66A39F8A&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=skfclVSbtokqhQWYyePZD&exp=1721617274908&sig=xxTik_Gr31NpYQQypAugz4nX7jUW0aCEYxwMuxJZ06M&sec=AEHSH-zR6ohQPpOqmOYa9O8kjdLSpyEjZuTlt3nXcZY&iv=uzH12VYZO3VLNcrHAi1Diw", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452228905_367410816374471_7852385781784791159_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=aX5f4mGvnVIQ7kNvgGFEnA6&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYDuHgX-K40lC4NfV-6FW_E54eZF-gg4UTar_7TGcpL4uQ&oe=66A3A24E&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=MTTY4-HC1v2pg2F_q2YAU&exp=1721617274908&sig=VU8EFG_f7dHqga3EnXnLcH_oPKEeVNXPrqdlo0mD8Nk&sec=U5CgHCJaLwI2MOjpmo03s7vfj5QFxiFNVeDWcEP6fQM&iv=-RuZYNNMyTVp2KpIowq4bw", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452228911_3318139995154518_4883932498702162302_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=aGbLmpNayqoQ7kNvgFzXC8Q&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYA9hQTZzCf2xipkRdXnU56SKD8wbNWhMwebMjh4lqfBVw&oe=66A385F1&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=OMYBVhNfM8BeCRz7fO9Yz&exp=1721617274908&sig=TJPIY1mwaekQmeP1hfHAcfevFvzCdSLgXfe575QVxQg&sec=qg1lGIWEeJrncp19EoZhjEpnc-OiurkXjJziGsZjs68&iv=n4esZveKpCisEmOznN95wQ", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452210216_444024151553743_4298660291173762610_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=J7N6lN7o7cEQ7kNvgE6KZt7&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYA77uIq17CjYN2SDctFRrRFKcM5-EkPZ09TN_PAUq8djQ&oe=66A3AD3B&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=uaa09EirBgeRc9smyU_dX&exp=1721617274908&sig=A6deXS9kht3yN3m4pongohJFtcUazmB12XNslZN0kVg&sec=lhbb1o0aTs_gGZ3pEIy7ydicu0AuGht0rQku5g1gCF8&iv=KbnrpgngQpqitb7FYzGvfg", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/451967516_1278010893604917_3972666716732793527_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=HWEf7HyB_9UQ7kNvgFzPYTi&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYBhTYB1QdcL3H3MoVHAnD-9shUX4Y4jht342fniiIzmtw&oe=66A3B7C4&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=NijzBQbIGtSoa0asqOT7v&exp=1721617274908&sig=ebNOcPTJ3BPbg33maKAYESWezCE0j3x-2KC8VHPwvsg&sec=tRAkTt_12QkCcS4-RhHJ30V5OxF6t_blMjW9DZQ9KVU&iv=v8IaGCcNQy92xB8yDihEQQ", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452227040_1206333037189915_458173170853920963_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=zcTyim1JTTgQ7kNvgFwVA4S&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYClIaYa5EtT4XAk1PG0h-PC4JiqxWZLmXi1ulbl8JHU8A&oe=66A385C6&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=ap1VltMxKT-I1N8LL-T2d&exp=1721617274908&sig=FUo2bi8p7BizOIfZpS-oEvZFmDrSuYpKawC7vWz8xe0&sec=nuoAQpuiY51jbDkdsjLL0nKWq4NbaSGqSxXp5ER0ZUE&iv=6F4lINOeix3pYiJA401ueA", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/452227038_1402442430442706_1020569842860299930_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=OH3J4VrlbqsQ7kNvgGa1DpO&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYCrTOODetIGET116j3wLDkhpTK0Zh3kEn_7Wo61xw-EIw&oe=66A3A5B5&_nc_sid=10d13b"}, %{"thumb" => "https://olly.imput.net/api/stream?id=zNBU426Ti02ICVC7lliAL&exp=1721617274909&sig=Vdyh6dUubrFhqoIlYK_HDaefABCPGSCdXMG7jVEJzlI&sec=zh_dx1yDm2AfeIkgW-4K5BOOp6EYxJPKilTyXC2pkfs&iv=eaRyms-lKpzhEtDa705aUw", "type" => "photo", "url" => "https://scontent.cdninstagram.com/v/t51.29350-15/451963306_811492997822997_1272700778613563056_n.heic?stp=dst-jpg_e35_s1080x1080&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=CgES431AWXIQ7kNvgFQdWoN&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYBCW3JSU2k1x68dnQQ4fuuq43NNZn1ujiY4sqnmoVEOIw&oe=66A39D0D&_nc_sid=10d13b"}], "pickerType" => "various", "status" => "picker"} -``` - -# telegram bot limit: only ont bot instance - -```elixir -%{ok: false, description: "Conflict: terminated by other getUpdates request; make sure that only one bot instance is running", error_code: 409} -``` - -# 2023-09-15 - -issues: fly deploy --remote-only fails with: - -```log - ** (EXIT) an exception was raised: - ** (UndefinedFunctionError) function Tesla.Adapter.Hackney.call/2 is undefined (module Tesla.Adapter.Hackney is not available) -``` - -resolved by - -```sh -fly deploy --remote-only --no-cache -``` - -docs: - -- [Continuous Deployment with Fly.io and GitHub Actions · Fly Docs](https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/) diff --git a/docs/dev_readme.md b/docs/dev_readme.md deleted file mode 100644 index ec2bf80..0000000 --- a/docs/dev_readme.md +++ /dev/null @@ -1,21 +0,0 @@ -# Dev Readme - -## Dev - -```sh -./dev -``` - -## Prod - -```sh -./start -``` - -Stop - -```bash -ps aux | grep mix - -kill -9 -``` diff --git a/docs/error_log_download_youtube_big_video.png b/docs/error_log_download_youtube_big_video.png deleted file mode 100644 index b5b45df..0000000 Binary files a/docs/error_log_download_youtube_big_video.png and /dev/null differ diff --git "a/docs/error_log_file path \344\270\215\350\203\275\345\220\253\346\234\211\345\244\252\345\244\232\347\211\271\346\256\212\345\255\227\347\254\246\357\274\214\344\276\213\345\246\202\346\226\234\346\235\240.png" "b/docs/error_log_file path \344\270\215\350\203\275\345\220\253\346\234\211\345\244\252\345\244\232\347\211\271\346\256\212\345\255\227\347\254\246\357\274\214\344\276\213\345\246\202\346\226\234\346\235\240.png" deleted file mode 100644 index 79b4295..0000000 Binary files "a/docs/error_log_file path \344\270\215\350\203\275\345\220\253\346\234\211\345\244\252\345\244\232\347\211\271\346\256\212\345\255\227\347\254\246\357\274\214\344\276\213\345\246\202\346\226\234\346\235\240.png" and /dev/null differ diff --git a/docs/error_log_streaming_data.png b/docs/error_log_streaming_data.png deleted file mode 100644 index 36fb821..0000000 Binary files a/docs/error_log_streaming_data.png and /dev/null differ diff --git a/docs/ex_gram_input.png b/docs/ex_gram_input.png deleted file mode 100644 index a71855e..0000000 Binary files a/docs/ex_gram_input.png and /dev/null differ diff --git a/docs/file_faster_than_db.png b/docs/file_faster_than_db.png deleted file mode 100644 index 79784c6..0000000 Binary files a/docs/file_faster_than_db.png and /dev/null differ diff --git a/docs/ideas.md b/docs/ideas.md deleted file mode 100644 index cd49f8f..0000000 --- a/docs/ideas.md +++ /dev/null @@ -1,16 +0,0 @@ -# ideas - -- [ ] 为什么我不用 download url 作为文件名呢? - - 优点:可以避免重复下载,提高用户体验以及 storage - - 缺点: - - 1. 无法直接观看视频和图片,因为文件后缀不对。 - - 1. 有一个解决方案:可以使用 filename.url 格式,url 即可。 - 2. Q: elixir 读区 file 并判断文件名是否存在,是否比 database select 快? - - ![ChatGPT answer: file existed faster than db](./file_faster_than_db.png) - -- [ ] memo: filename 可以使用空格区分,#设计参考 macos history diff --git a/docs/todos.md b/docs/todos.md deleted file mode 100644 index c36e74c..0000000 --- a/docs/todos.md +++ /dev/null @@ -1,46 +0,0 @@ -# Todos - -DevOps - -- [ ] ssh & scp skip password, setup token to ssh and scp - -small refactor: - -features - -- [ ] list all ins response - - [ ] image - - [ ] images - - [ ] video - - [ ] videos - - [ ] images and videos - - [ ] with url I should download? - -big task - -- [x] download ins videos and _images_ - - [ ] download multiple files in one task -- [ ] make it fast and with UI progress bar - -# Test cases - -- message - - - text -> urls - -- x.com - - - download single image - - download multiple images - - download video - - download videos - - download images and videos by one link - - download better file base on smart phone or pc. - -- ins - - single image - - images - - video - - videos - - images and videos - - best file for smart phone and pc diff --git a/fly.toml b/fly.toml deleted file mode 100644 index 5f80be5..0000000 --- a/fly.toml +++ /dev/null @@ -1,22 +0,0 @@ -# fly.toml app configuration file generated for aier-bot on 2023-09-15T13:17:27+09:00 -# -# See https://fly.io/docs/reference/configuration/ for information about how to use this file. -# - -app = "aier-bot" -primary_region = "nrt" - -[build] - builder = "heroku/buildpacks:20" - buildpacks = ["https://cnb-shim.herokuapp.com/v1/hashnuke/elixir"] - -[env] - PORT = "8080" - -[http_service] - internal_port = 8080 - force_https = true - auto_stop_machines = true - auto_start_machines = true - min_machines_running = 0 - processes = ["app"] diff --git a/lib/aier_bot/aier_api.ex b/lib/aier_bot/aier_api.ex deleted file mode 100644 index 4613cd1..0000000 --- a/lib/aier_bot/aier_api.ex +++ /dev/null @@ -1,23 +0,0 @@ -defmodule AierBot.AierApi do - use Tesla - - plug(Tesla.Middleware.BaseUrl, "https://www.aier.app") - plug(Tesla.Middleware.JSON) - - def aier_api_token do - Application.fetch_env!(:aier_bot, :aier_api_token) - end - - def create_memo(memo) do - url = Path.join("/api/webhooks/memoCreate", aier_api_token()) - - body = %{ - content: memo, - created_at: DateTime.to_iso8601(DateTime.utc_now()), - # TODO: real source_url - source_url: "telegram bot" - } - - post(url, body) - end -end diff --git a/lib/aier_bot/openai_api.ex b/lib/aier_bot/openai_api.ex deleted file mode 100644 index 3b2a074..0000000 --- a/lib/aier_bot/openai_api.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule AierBot.OpenaiApi do - alias OpenaiEx.Image - - def openai() do - api_key = Application.fetch_env!(:aier_bot, :openai_api_key) - openai = OpenaiEx.new(api_key) - - openai - end - - def image_generation(prompt) do - openai = openai() - - fetch_blob = fn url -> - Finch.build(:get, url) - |> Finch.request!(OpenaiEx.Finch) - |> Map.get(:body) - end - - images = - openai - |> Image.create(%{ - prompt: prompt, - n: 2, - size: "1024x1024" - }) - |> Map.get("data") - |> Enum.map(fn x -> x["url"] |> fetch_blob.() end) - - {:ok, %{data: images}} - end -end diff --git a/lib/aier_bot/application.ex b/lib/save_it/application.ex similarity index 63% rename from lib/aier_bot/application.ex rename to lib/save_it/application.ex index b201dfc..fd779cd 100644 --- a/lib/aier_bot/application.ex +++ b/lib/save_it/application.ex @@ -1,4 +1,4 @@ -defmodule AierBot.Application do +defmodule SaveIt.Application do # See https://hexdocs.pm/elixir/Application.html # for more information on OTP Applications @moduledoc false @@ -7,16 +7,16 @@ defmodule AierBot.Application do @impl true def start(_type, _args) do - token = Application.fetch_env!(:aier_bot, :telegram_bot_token) + token = Application.fetch_env!(:save_it, :telegram_bot_token) children = [ ExGram, - {AierBot.Bot, [method: :polling, token: token]} + {SaveIt.Bot, [method: :polling, token: token]} ] # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options - opts = [strategy: :one_for_one, name: AierBot.Supervisor] + opts = [strategy: :one_for_one, name: SaveIt.Supervisor] Supervisor.start_link(children, opts) end end diff --git a/lib/aier_bot/bot.ex b/lib/save_it/bot.ex similarity index 93% rename from lib/aier_bot/bot.ex rename to lib/save_it/bot.ex index a9c7e27..74cd855 100644 --- a/lib/aier_bot/bot.ex +++ b/lib/save_it/bot.ex @@ -1,9 +1,9 @@ -defmodule AierBot.Bot do +defmodule SaveIt.Bot do require Logger - alias AierBot.CobaltClient - alias AierBot.FileHelper - alias AierBot.GoogleDrive - alias AierBot.GoogleOAuth2DeviceFlow + alias SaveIt.CobaltClient + alias SaveIt.FileHelper + alias SaveIt.GoogleDrive + alias SaveIt.GoogleOAuth2DeviceFlow @bot :save_it_bot @@ -113,18 +113,6 @@ defmodule AierBot.Bot do end end - # def handle({:command, :files, %{chat: chat}}, _context) do - # case GoogleDrive.list_files(chat.id) do - # {:ok, files} -> - # send_message(chat.id, """ - # Files: - # #{Enum.map(files, fn file -> file["name"] end) |> Enum.join("\n")} - # """) - # {:error, error} -> - # IO.inspect(error) - # end - # end - def handle({:command, :folder, %{chat: chat, text: text}}, _context) do case text do nil -> @@ -144,7 +132,6 @@ defmodule AierBot.Bot do unless Enum.empty?(urls) do {:ok, progress_message} = send_message(chat.id, Enum.at(@progress, 0)) - # TODO: for payment, free only one url, for multiple urls, need to pay url = List.first(urls) case CobaltClient.get_download_url(url) do @@ -152,7 +139,7 @@ defmodule AierBot.Bot do case FileHelper.get_downloaded_files(download_urls) do nil -> update_message(chat.id, progress_message.message_id, Enum.slice(@progress, 0..1)) - # TODO: + case FileHelper.download_files(download_urls) do {:ok, files} -> update_message( @@ -161,7 +148,7 @@ defmodule AierBot.Bot do Enum.slice(@progress, 0..2) ) - # TODO: + # TODO: send media group # bot_send_media_group(chat.id, files) bot_send_files(chat.id, files) diff --git a/lib/aier_bot/cobalt_client.ex b/lib/save_it/cobalt_client.ex similarity index 96% rename from lib/aier_bot/cobalt_client.ex rename to lib/save_it/cobalt_client.ex index 97f3d11..6b3eae9 100644 --- a/lib/aier_bot/cobalt_client.ex +++ b/lib/save_it/cobalt_client.ex @@ -1,4 +1,4 @@ -defmodule AierBot.CobaltClient do +defmodule SaveIt.CobaltClient do require Logger use Tesla @@ -35,7 +35,6 @@ defmodule AierBot.CobaltClient do {:ok, url} %{"status" => "picker", "picker" => picker_items} -> - IO.inspect(picker_items, label: "picker_items") # [%{"url" => url}] = picker_items # error: you attempted to apply a function named :first on [], If you are using Kernel.apply/3, make sure the module is an atom. If you are using the dot syntax, such as module.function(), make sure the left-hand side of the dot is an atom representing a module {:ok, url, Enum.map(picker_items, &Map.get(&1, "url"))} diff --git a/lib/aier_bot/file_helper.ex b/lib/save_it/file_helper.ex similarity index 99% rename from lib/aier_bot/file_helper.ex rename to lib/save_it/file_helper.ex index ac5bc4a..93952c2 100644 --- a/lib/aier_bot/file_helper.ex +++ b/lib/save_it/file_helper.ex @@ -1,4 +1,4 @@ -defmodule AierBot.FileHelper do +defmodule SaveIt.FileHelper do require Logger use Tesla diff --git a/lib/aier_bot/google_drive.ex b/lib/save_it/google_drive.ex similarity index 97% rename from lib/aier_bot/google_drive.ex rename to lib/save_it/google_drive.ex index a6095ec..d1f1e8a 100644 --- a/lib/aier_bot/google_drive.ex +++ b/lib/save_it/google_drive.ex @@ -1,13 +1,11 @@ -defmodule AierBot.GoogleDrive do +defmodule SaveIt.GoogleDrive do @moduledoc """ TODO: - [ ] list folders - [ ] select folder and save folder_id """ - alias AierBot.FileHelper - + alias SaveIt.FileHelper require Logger - use Tesla plug(Tesla.Middleware.BaseUrl, "https://www.googleapis.com") @@ -126,7 +124,6 @@ defmodule AierBot.GoogleDrive do body: %{"files" => files} }} ) do - IO.puts("handle_response, files: #{inspect(files)}") {:ok, files} end diff --git a/lib/aier_bot/google_oauth2_device_flow.ex b/lib/save_it/google_oauth2_device_flow.ex similarity index 74% rename from lib/aier_bot/google_oauth2_device_flow.ex rename to lib/save_it/google_oauth2_device_flow.ex index 77b47fd..28223ab 100644 --- a/lib/aier_bot/google_oauth2_device_flow.ex +++ b/lib/save_it/google_oauth2_device_flow.ex @@ -1,4 +1,5 @@ -defmodule AierBot.GoogleOAuth2DeviceFlow do +defmodule SaveIt.GoogleOAuth2DeviceFlow do + require Logger use Tesla plug(Tesla.Middleware.BaseUrl, "https://oauth2.googleapis.com") @@ -8,8 +9,8 @@ defmodule AierBot.GoogleOAuth2DeviceFlow do {"Content-Type", "application/x-www-form-urlencoded"} ]) - @client_id System.fetch_env!("GOOGLE_OAUTH_CLIENT_ID") - @client_secret System.fetch_env!("GOOGLE_OAUTH_CLIENT_SECRET") + @client_id Application.compile_env(:save_it, :google_oauth_client_id) + @client_secret Application.compile_env(:save_it, :google_oauth_client_secret) @device_code_url "/device/code" @token_url "/token" @@ -24,17 +25,16 @@ defmodule AierBot.GoogleOAuth2DeviceFlow do end defp handle_response({:ok, %Tesla.Env{status: 200, body: body}}) do - IO.puts("handle_response, body: #{inspect(body)}") {:ok, body} end defp handle_response({:ok, %Tesla.Env{status: status, body: body}}) do - IO.puts("handle_response, status: #{status}, body: #{inspect(body)}") + Logger.warning("handle_response, status: #{status}, body: #{inspect(body)}") {:error, %{status: status, body: body}} end defp handle_response({:error, reason}) do - IO.puts("handle_response, reason: #{inspect(reason)}") + Logger.error("handle_response, reason: #{inspect(reason)}") {:error, reason} end diff --git a/mix.exs b/mix.exs index 0019bf7..bacf8be 100644 --- a/mix.exs +++ b/mix.exs @@ -1,13 +1,12 @@ -defmodule AierBot.MixProject do +defmodule SaveIt.MixProject do use Mix.Project def project do [ - app: :aier_bot, + app: :save_it, version: "0.1.0", - elixir: "~> 1.15", - start_permanent: Mix.env() == :prod, - deps: deps() + deps: deps(), + start_permanent: Mix.env() == :prod ] end @@ -15,18 +14,16 @@ defmodule AierBot.MixProject do def application do [ extra_applications: [:logger], - mod: {AierBot.Application, []} + mod: {SaveIt.Application, []} ] end - # Run "mix help deps" to learn about dependencies. defp deps do [ - {:ex_gram, "~> 0.40.0"}, - {:tesla, "~> 1.11.1"}, - {:openai_ex, "~> 0.3.0"}, - {:hackney, "~> 1.18.2"}, - {:jason, ">= 1.4.2"} + {:ex_gram, "~> 0.53"}, + {:tesla, "~> 1.11"}, + {:hackney, "~> 1.12"}, + {:jason, "~> 1.4.1"} ] end end diff --git a/mix.lock b/mix.lock index 5165666..6117df1 100644 --- a/mix.lock +++ b/mix.lock @@ -1,23 +1,14 @@ %{ - "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "ex_gram": {:hex, :ex_gram, "0.40.0", "ccedb8e4ad0b7e24de007614de447b54b2eff5398336ca18ac5e31cc08846927", [:mix], [{:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.12", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:maxwell, "~> 2.3.1", [hex: :maxwell, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:tesla, "~> 1.2", [hex: :tesla, repo: "hexpm", optional: true]}], "hexpm", "1a518574f91101188b0fd76cba760e36c3516b79b31e6bef056305bb9ad1a66e"}, - "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, - "hackney": {:hex, :hackney, "1.18.2", "d7ff544ddae5e1cb49e9cf7fa4e356d7f41b283989a1c304bfc47a8cc1cf966f", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "af94d5c9f97857db257090a4a10e5426ecb6f4918aa5cc666798566ae14b65fd"}, - "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, + "ex_gram": {:hex, :ex_gram, "0.53.0", "3e01be7a3e31c8ebb896b13ab38e38505481ddab385c0228e4c41da3105e4e15", [:mix], [{:gun, "~> 2.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.20", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:tesla, "~> 1.2", [hex: :tesla, repo: "hexpm", optional: true]}], "hexpm", "c0ba891c35cbb30a5e960f06ce1011de8e997f433df838d39b331ea3717d5cc4"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, - "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, - "multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"}, - "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, - "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, - "openai_ex": {:hex, :openai_ex, "0.3.0", "44e3684c1bbd1d96d0cc5d96617fba3591658d502365b4c0d85d5691362e11df", [:mix], [{:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: false]}], "hexpm", "9c9e579e64882eb8a714ac446b4e861f011d92ec5a89d91db200ad5892b6d3e2"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "tesla": {:hex, :tesla, "1.11.2", "24707ac48b52f72f88fc05d242b1c59a85d1ee6f16f19c312d7d3419665c9cd5", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "c549cd03aec6a7196a641689dd378b799e635eb393f689b4bd756f750c7a4014"}, + "tesla": {:hex, :tesla, "1.12.1", "fe2bf4250868ee72e5d8b8dfa408d13a00747c41b7237b6aa3b9a24057346681", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2391efc6243d37ead43afd0327b520314c7b38232091d4a440c1212626fdd6e7"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } diff --git a/test/aier_bot_test.exs b/test/aier_bot_test.exs deleted file mode 100644 index 08508f1..0000000 --- a/test/aier_bot_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule AierBotTest do - use ExUnit.Case - doctest AierBot - - test "greets the world" do - assert AierBot.hello() == :world - end -end diff --git a/test/save_it_test.exs b/test/save_it_test.exs new file mode 100644 index 0000000..20cc620 --- /dev/null +++ b/test/save_it_test.exs @@ -0,0 +1,8 @@ +defmodule SaveItTest do + use ExUnit.Case + doctest SaveIt + + test "greets the world" do + assert SaveIt.hello() == :world + end +end