Skip to content

sedipsek/Iris

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

254 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Iris - ์•ˆ๋“œ๋กœ์ด๋“œ ๋„ค์ดํ‹ฐ๋ธŒ DB๊ธฐ๋ฐ˜ ๋ด‡ ํ”„๋ ˆ์ž„์›Œํฌ

์ด ํ”„๋กœ์ ํŠธ๋Š” ์นด์นด์˜คํ†ก ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๋™ํ•˜์—ฌ HTTP ๊ธฐ๋ฐ˜ ์ฑ„ํŒ… ๋ด‡์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ์ƒํƒœ: ๋ฒ ํƒ€

์‹œ์ž‘ํ•˜๊ธฐ

ํ•„์š” ์กฐ๊ฑด

  • ์•ˆ๋“œ๋กœ์ด๋“œ ๊ธฐ๊ธฐ: ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์นด์นด์˜คํ†ก์ด ์„ค์น˜๋˜์–ด ์žˆ๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ๊ธฐ๊ธฐ์—์„œ ์‹คํ–‰๋˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฃจํŠธ ๊ถŒํ•œ: ์นด์นด์˜คํ†ก ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ผ๋ถ€ ์‹œ์Šคํ…œ ์„œ๋น„์Šค์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ๋ฃจํŠธ ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • HTTP ์„œ๋ฒ„ ๋˜๋Š” WebSocket ํด๋ผ์ด์–ธํŠธ: Iris์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ณ„๋„์˜ HTTP ์„œ๋ฒ„๋‚˜ WebSocket ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์„ค์น˜

  1. ์ตœ์‹  Iris๋ฅผ Releases์—์„œ ๋‹ค์šด๋กœ๋“œํ•˜์„ธ์š”.

  2. ํŒŒ์ผ ๋ณต์‚ฌ: adb๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Iris apk ํŒŒ์ผ์„ ์•ˆ๋“œ๋กœ์ด๋“œ ํ™˜๊ฒฝ์— ๋ณต์‚ฌํ•˜์„ธ์š”.

    adb push Iris.apk /data/local/tmp
  3. apk ํŒŒ์ผ ์‹คํ–‰:

    iris_control์„ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ์„ธ์š”.(์œˆ๋„์šฐ ์ด์šฉ์ž๋Š” skip)

    chmod +x iris_control

    ์‹คํ–‰ํ•˜๋ ค๋ฉด iris_control์„ ์‚ฌ์šฉํ•˜์„ธ์š”.(์œˆ๋„์šฐ ์ด์šฉ์ž๋Š” ./iris_control.ps1)

    ./iris_control start

    iris_control์€ install/start/status/stop ๋ช…๋ น์–ด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  4. Config ์„ค์ •:

    http://[ANDROID_IP]:3000/dashboard ์— ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•ด ์ ‘์†ํ•˜์—ฌ, ์„ค์ •์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•

Iris๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ HTTP ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ์ •๋ณด๋ฅผ ์ฃผ๊ณ  ๋ฐ›์Šต๋‹ˆ๋‹ค.

HTTP API ์—”๋“œํฌ์ธํŠธ

๋ชจ๋“  ์š”์ฒญ์€ ๋ณ„๋„๋กœ ๋ช…์‹œ๋˜์ง€ ์•Š๋Š” ํ•œ Content-Type: application/json๊ณผ ํ•จ๊ป˜ POST ์š”์ฒญ์œผ๋กœ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • /reply: ์นด์นด์˜คํ†ก ์ฑ„ํŒ…๋ฐฉ์— ๋ฉ”์‹œ์ง€ ๋˜๋Š” ์‚ฌ์ง„์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    {
      "type": "text",  // ๋˜๋Š” "image", "image_multiple"
      "room": "[CHAT_ROOM_ID]", // ์ฑ„ํŒ…๋ฐฉ ID (๋ฌธ์ž์—ด)
      "data": "[MESSAGE_TEXT]"  // ํ…์ŠคํŠธ ๋ฉ”์‹œ์ง€์˜ ๊ฒฝ์šฐ
                                // ์ด๋ฏธ์ง€ ๋ฉ”์‹œ์ง€์˜ ๊ฒฝ์šฐ Base64 ์ธ์ฝ”๋”ฉ๋œ ์ด๋ฏธ์ง€ ๋ฌธ์ž์—ด
                                // ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€ ๋ฉ”์‹œ์ง€์˜ ๊ฒฝ์šฐ Base64 ์ธ์ฝ”๋”ฉ๋œ ์ด๋ฏธ์ง€ ๋ฌธ์ž์—ด์˜ ๋ฆฌ์ŠคํŠธ
    }

    ์˜ˆ์‹œ (ํ…์ŠคํŠธ ๋ฉ”์‹œ์ง€):

    curl -X POST -H "Content-Type: application/json" -d '{"type": "text", "room": "1234567890", "data": "SendMsgDB์—์„œ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€!"}' http://[YOUR_DEVICE_IP]:[bot_http_port]/reply

    ์˜ˆ์‹œ (์ด๋ฏธ์ง€ ๋ฉ”์‹œ์ง€):

    curl -X POST -H "Content-Type: application/json" -d '{"type": "image", "room": "1234567890", "data": "[BASE64_ENCODED_IMAGE_DATA]"}' http://[YOUR_DEVICE_IP]:[bot_http_port]/reply
    curl -X POST -H "Content-Type: application/json" -d '{"type": "image_multiple", "room": "1234567890", "data": [BASE64_ENCODED_IMAGE_DATA1,BASE64_ENCODED_IMAGE_DATA2,BASE64_ENCODED_IMAGE_DATA3]}' http://[YOUR_DEVICE_IP]:[bot_http_port]/reply
  • /query: ์นด์นด์˜คํ†ก ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— SQL ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” ์‘๋‹ต์—์„œ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ ํ•„๋“œ๋ฅผ ์ž๋™์œผ๋กœ ๋ณตํ˜ธํ™”ํ•ฉ๋‹ˆ๋‹ค.

    message ๋˜๋Š” attachment๋ฅผ user_id ๋ฐ enc์™€ ํ•จ๊ป˜ ์ฟผ๋ฆฌํ•˜๋ฉด ๋ณตํ˜ธํ™”๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. nickname, profile_image_url, full_profile_image_url ๋˜๋Š” original_profile_image_url์„ enc์™€ ํ•จ๊ป˜ ์ฟผ๋ฆฌํ•˜๋ฉด ๋ณตํ˜ธํ™”๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    // ๋‹จ์ผ ์š”์ฒญ
    {
      "query": "[SQL_QUERY]",  // SQL ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด
      "bind": ["[BINDING_VALUE_1]", "[BINDING_VALUE_2]", ...] // ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์„ ํƒ์  ๋ฐ”์ธ๋”ฉ
    }

    ์˜ˆ์‹œ (๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•œ ๋‹จ์ผ ์ฟผ๋ฆฌ):

    curl -X POST -H "Content-Type: application/json" -d '{"query": "SELECT _id, chat_id, user_id, message FROM chat_logs WHERE user_id = ? ORDER BY _id DESC LIMIT 5", "bind": ["1234567890"]}' http://[YOUR_DEVICE_IP]:[bot_http_port]/query

    ์˜ˆ์‹œ (๋ฒŒํฌ ์ฟผ๋ฆฌ):

    curl -X POST -H "Content-Type: application/json" -d '{"queries": [{"query": "SELECT _id, chat_id, user_id, message FROM chat_logs ORDER BY _id DESC LIMIT 5", "bind": []}, {"query": "SELECT name FROM db2.friends LIMIT 2", "bind": []}]}' http://[YOUR_DEVICE_IP]:[bot_http_port]/query

    ์‘๋‹ต (JSON):

    {
      "data": [
          // ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ๋ฐฐ์—ด, ๊ฐ ๊ฒฐ๊ณผ๋Š” ์—ด ์ด๋ฆ„๊ณผ ๊ฐ’์˜ ๋งต์ž…๋‹ˆ๋‹ค.
          {
            "_id": "...",
            "chat_id": "...",
            "user_id": "...",
            "message": "...", // ํ•ด๋…๋œ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
            // ... ๊ธฐํƒ€ ์—ด ...
          },
          // ... ๋” ๋งŽ์€ ๊ฒฐ๊ณผ ...
      ]
    }
  • /decrypt: ์นด์นด์˜คํ†ก ๋ฉ”์‹œ์ง€๋ฅผ ๋ณตํ˜ธํ™”ํ•ฉ๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    {
      "enc": [ENCRYPTION_TYPE], // ์•”ํ˜ธํ™” ์œ ํ˜• (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ ธ์˜จ ์ •์ˆ˜)
      "b64_ciphertext": "[BASE64_ENCODED_CIPHERTEXT]", // Base64 ์ธ์ฝ”๋”ฉ๋œ ์•”ํ˜ธํ™”๋œ ๋ฉ”์‹œ์ง€
      "user_id": [USER_ID] // ์‚ฌ์šฉ์ž ID (long integer)
    }

    ์˜ˆ์‹œ:

    curl -X POST -H "Content-Type: application/json" -d '{"enc": 0, "b64_ciphertext": "[ENCRYPTED_MESSAGE_BASE64]", "user_id": 1234567890}' http://[YOUR_DEVICE_IP]:[bot_http_port]/decrypt

    ์‘๋‹ต (JSON):

    {
      "plain_text": "[DECRYPTED_MESSAGE_TEXT]"
    }
  • /aot (GET): aot ๊ด€๋ จ ํ† ํฐ์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

    ์˜ˆ์‹œ:

    curl -X GET http://[YOUR_DEVICE_IP]:[bot_http_port]/aot

    ์‘๋‹ต (JSON):

    {
      "success": true,
      "aot" : {
        "access_token" : String,
        "refresh_token" : String,
        "token_type" : String,
        "d_id" : String
      }
    }

์„ค์ • API ์—”๋“œํฌ์ธํŠธ

  • /dashboard (GET): Iris ๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•œ ์›น UI๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ด URL์„ ์—ด์–ด ์„ค์ •์„ ์ˆ˜์ •ํ•˜์„ธ์š”.

    ์˜ˆ์‹œ:

    # ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์—ฌ์„ธ์š”
    http://[YOUR_DEVICE_IP]:[bot_http_port]/dashboard

    ์ด ์—”๋“œํฌ์ธํŠธ๋Š” ์›น ์„œ๋ฒ„ ์—”๋“œํฌ์ธํŠธ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋ง ์†๋„, ๋ฉ”์‹œ์ง€ ์ „์†ก ์†๋„์™€ ๊ฐ™์€ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉ์ž ์นœํ™”์ ์ธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๋ณด๊ณ  ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์›น ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • /config (GET): ํ˜„์žฌ ๊ตฌ์„ฑ์„ JSON ์‘๋‹ต์œผ๋กœ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ํ™œ์„ฑ ์„ค์ •์„ ํ™•์ธํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ์˜ˆ์‹œ:

    curl http://[YOUR_DEVICE_IP]:[bot_http_port]/config

    ์‘๋‹ต (JSON):

    {
      "bot_name": "[YOUR_BOT_NAME]",
      "bot_http_port": [PORT_FOR_HTTP_SERVER],
      "web_server_endpoint": "[YOUR_WEB_SERVER_URL_FOR_MESSAGE_FORWARDING],
      "db_polling_rate": [DATABASE_POLLING_INTERVAL_IN_MILLISECONDS],
      "message_send_rate": [MESSAGE_SEND_INTERVAL_IN_MILLISECONDS],
      "bot_id": [YOUR_KAKAO_TALK_USER_ID]
    }
  • /config/endpoint (POST): ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์„ ์œ„ํ•œ ์›น ์„œ๋ฒ„ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    {
      "endpoint": "[YOUR_WEB_SERVER_URL]"
    }

    ์˜ˆ์‹œ:

    curl -X POST -H "Content-Type: application/json" -d '{"endpoint": "http://192.168.1.100:5000/new_messages"}' http://[YOUR_DEVICE_IP]:[bot_http_port]/config/endpoint
  • /config/dbrate (POST): ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํด๋ง ์†๋„๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์„ ์กฐ์ •ํ•˜๋ฉด Iris๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒˆ ๋ฉ”์‹œ์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ๋นˆ๋„๊ฐ€ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ๊ฐ’์ด ๋‚ฎ์„์ˆ˜๋ก CPU ์‚ฌ์šฉ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•˜์ง€๋งŒ ๋ฉ”์‹œ์ง€ ๊ฐ์ง€๊ฐ€ ๋” ์ฆ‰๊ฐ์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    {
      "rate": [DATABASE_POLLING_INTERVAL_IN_MILLISECONDS]
    }

    ์˜ˆ์‹œ:

    curl -X POST -H "Content-Type: application/json" -d '{"rate": 300}' http://[YOUR_DEVICE_IP]:[bot_http_port]/config/dbrate
  • /config/sendrate (POST): ๋ฉ”์‹œ์ง€ ์ „์†ก ์†๋„๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์นด์นด์˜คํ†ก์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ์ตœ์†Œ ๊ฐ„๊ฒฉ์„ ์ œ์–ดํ•˜์—ฌ ์ „์†ก ๋นˆ๋„๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    {
      "rate": [MESSAGE_SEND_INTERVAL_IN_MILLISECONDS]
    }

    ์˜ˆ์‹œ:

    curl -X POST -H "Content-Type: application/json" -d '{"rate": 200}' http://[YOUR_DEVICE_IP]:[bot_http_port]/config/sendrate
  • /config/botport (POST): ๋ด‡ HTTP ์„œ๋ฒ„ ํฌํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ : ์ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ ์šฉํ•˜๋ ค๋ฉด Iris๋ฅผ ์žฌ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ์š”์ฒญ ๋ณธ๋ฌธ (JSON):

    {
      "port": [NEW_PORT_NUMBER]
    }

    ์˜ˆ์‹œ:

    curl -X POST -H "Content-Type: application/json" -d '{"port": 3001}' http://[YOUR_DEVICE_IP]:[bot_http_port]/config/botport

WebSocket ์—”๋“œํฌ์ธํŠธ

  • /ws: WebSocket ์—ฐ๊ฒฐ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์„ ์œ„ํ•œ API ๋ ˆํผ๋Ÿฐ์Šค

Iris๊ฐ€ ์นด์นด์˜คํ†ก ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒˆ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ์ง€ํ•˜๋ฉด /config/endpoint API๋ฅผ ํ†ตํ•ด ๊ตฌ์„ฑ๋œ web_server_endpoint๋กœ POST ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ๋˜ํ•œ, /ws๋ฅผ ํ†ตํ•ด WebSocket์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค๋ฉด, WebSocket์œผ๋กœ๋„ ์•„๋ž˜์˜ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

{
  "msg": "[DECRYPTED_MESSAGE_CONTENT]", // ๋ณตํ˜ธํ™”๋œ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
  "room": "[CHAT_ROOM_NAME]",          // ์ฑ„ํŒ…๋ฐฉ ์ด๋ฆ„ ๋˜๋Š” 1:1 ์ฑ„ํŒ…์˜ ๊ฒฝ์šฐ ๋ฐœ์‹ ์ž ์ด๋ฆ„
  "sender": "[SENDER_NAME]",          // ๋ฉ”์‹œ์ง€ ๋ฐœ์‹ ์ž ์ด๋ฆ„
  "json": {                            // 'chat_logs' ํ…Œ์ด๋ธ”์˜ ์›์‹œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ–‰ (JSON ํ˜•์‹)
    "_id": "...",
    "chat_id": "...",
    "user_id": "...",
    "message": "[DECRYPTED_MESSAGE_CONTENT]", // ๋ณตํ˜ธํ™”๋œ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ, "msg" ํ•„๋“œ์™€ ๋™์ผ
    "attachment": "[DECRYPTED_ATTACHMENT_INFO]", // ๋ณตํ˜ธํ™”๋œ attachment ๋‚ด์šฉ 
    "v": "{\"enc\": 0, ...}",           // ์›๋ž˜ 'v' ์—ด ๊ฐ’ (JSON ํ˜•์‹)
    // ... chat_logs ํ…Œ์ด๋ธ”์˜ ๊ธฐํƒ€ ์—ด ...
  }
}

Credits

  • SendMsg & Initial Concept: Based on the work of ye-seola/go-kdb.
  • KakaoTalk Decryption Logic: Decryption methods from jiru/kakaodecrypt.

Disclaimer

This project is provided for educational and research purposes only. The developers are not responsible for any misuse or damage caused by this software. Use it at your own risk and ensure you comply with all applicable laws and terms of service.

License

This project contains a mix of MIT-licensed and GPL-licensed code.

  • Original Code: All files in this repository, unless otherwise noted, are licensed under the MIT License. See LICENSE-MIT for details.
  • Third-Party Code: The specific file located at src/main/java/party/qwer/iris/NotificationPoller.kt is partially derived from an external project and is licensed under the GNU General Public License v3.0. See LICENSE-GPL for details.

Important: Because this project compiles together with GPL v3.0 code, the final compiled application as a whole is subject to the terms of the GPL v3.0.

About

An Android native db observer / message broker / reply sender for Kakaotalk bot

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Kotlin 58.5%
  • HTML 25.7%
  • Shell 11.5%
  • PowerShell 4.3%