open_ai_bot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 359180ac972111694562d94a4afccd3e90299f08abbfa13d36a05c765af8f6e5
4
+ data.tar.gz: 43d9f020391a4166cade19e015d9de007c1a10e9ccce01826779244b350a2a5c
5
+ SHA512:
6
+ metadata.gz: fdf1dfdab85f011e4268fdfa29fe204ab40e88b907df142867b7c888ea9450a5eb0c87cc47f39dae8b77a479fba19b8bbf732447771d07ef37016c426491fec2
7
+ data.tar.gz: b1e0759c526c80a8b0edd401e23c0326bc779906c1f660c01ed0ed736332c6c0304dfcfd5270e67884aaf38db4e4c8bb3243341dfb548089fbcfa231e684f063
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ config.yaml
data/.rubocop.yml ADDED
@@ -0,0 +1,46 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 3.2.2
5
+ NewCops: enable
6
+ Exclude:
7
+ - test.rb
8
+
9
+ Layout/LineLength:
10
+ Max: 100
11
+
12
+ Layout/SpaceAroundEqualsInParameterDefault:
13
+ EnforcedStyle: no_space
14
+
15
+ Layout/SpaceInsideBlockBraces:
16
+ EnforcedStyleForEmptyBraces: space
17
+
18
+ Lint/EmptyBlock:
19
+ Exclude:
20
+ - spec/rubydium/mixins/command_macros_spec.rb
21
+
22
+ Metrics/BlockLength:
23
+ Exclude:
24
+ - spec/**/*
25
+
26
+ Metrics/MethodLength:
27
+ CountAsOne:
28
+ - array
29
+ - heredoc
30
+ - method_call
31
+ - hash
32
+
33
+ Style/BlockDelimiters:
34
+ EnforcedStyle: braces_for_chaining
35
+
36
+ Style/Documentation:
37
+ Enabled: false
38
+
39
+ Style/HashSyntax:
40
+ EnforcedShorthandSyntax: never
41
+
42
+ Style/IfUnlessModifier:
43
+ Enabled: false
44
+
45
+ Style/StringLiterals:
46
+ EnforcedStyle: double_quotes
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "down", "~> 5.4"
6
+ gem "rubydium", ">= 0.2.5"
7
+ gem "ruby-openai", "~> 5.1"
data/Gemfile.lock ADDED
@@ -0,0 +1,109 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.8.5)
5
+ public_suffix (>= 2.0.2, < 6.0)
6
+ async (2.6.4)
7
+ console (~> 1.10)
8
+ fiber-annotation
9
+ io-event (~> 1.1)
10
+ timers (~> 4.1)
11
+ base64 (0.1.1)
12
+ coderay (1.1.3)
13
+ concurrent-ruby (1.2.2)
14
+ console (1.23.2)
15
+ fiber-annotation
16
+ fiber-local
17
+ domain_name (0.5.20190701)
18
+ unf (>= 0.0.5, < 1.0.0)
19
+ down (5.4.1)
20
+ addressable (~> 2.8)
21
+ dry-core (1.0.1)
22
+ concurrent-ruby (~> 1.0)
23
+ zeitwerk (~> 2.6)
24
+ dry-inflector (1.0.0)
25
+ dry-logic (1.5.0)
26
+ concurrent-ruby (~> 1.0)
27
+ dry-core (~> 1.0, < 2)
28
+ zeitwerk (~> 2.6)
29
+ dry-struct (1.6.0)
30
+ dry-core (~> 1.0, < 2)
31
+ dry-types (>= 1.7, < 2)
32
+ ice_nine (~> 0.11)
33
+ zeitwerk (~> 2.6)
34
+ dry-types (1.7.1)
35
+ concurrent-ruby (~> 1.0)
36
+ dry-core (~> 1.0)
37
+ dry-inflector (~> 1.0)
38
+ dry-logic (~> 1.4)
39
+ zeitwerk (~> 2.6)
40
+ faraday (2.7.11)
41
+ base64
42
+ faraday-net_http (>= 2.0, < 3.1)
43
+ ruby2_keywords (>= 0.0.4)
44
+ faraday-multipart (1.0.4)
45
+ multipart-post (~> 2)
46
+ faraday-net_http (3.0.2)
47
+ ffi (1.15.5)
48
+ ffi-compiler (1.0.1)
49
+ ffi (>= 1.0.0)
50
+ rake
51
+ fiber-annotation (0.2.0)
52
+ fiber-local (1.0.0)
53
+ http (5.1.1)
54
+ addressable (~> 2.8)
55
+ http-cookie (~> 1.0)
56
+ http-form_data (~> 2.2)
57
+ llhttp-ffi (~> 0.4.0)
58
+ http-cookie (1.0.5)
59
+ domain_name (~> 0.5)
60
+ http-form_data (2.3.0)
61
+ ice_nine (0.11.2)
62
+ io-event (1.3.2)
63
+ llhttp-ffi (0.4.0)
64
+ ffi-compiler (~> 1.0)
65
+ rake (~> 13.0)
66
+ method_source (1.0.0)
67
+ multipart-post (2.3.0)
68
+ nokogiri (1.15.4-arm64-darwin)
69
+ racc (~> 1.4)
70
+ nokogiri (1.15.4-x86_64-linux)
71
+ racc (~> 1.4)
72
+ pry (0.14.2)
73
+ coderay (~> 1.1)
74
+ method_source (~> 1.0)
75
+ public_suffix (5.0.3)
76
+ racc (1.7.1)
77
+ rake (13.0.6)
78
+ ruby-openai (5.1.0)
79
+ faraday (>= 1)
80
+ faraday-multipart (>= 1)
81
+ ruby2_keywords (0.0.5)
82
+ rubydium (0.2.5)
83
+ async (~> 2.3)
84
+ telegram-bot-ruby (~> 1.0.0)
85
+ telegram-bot-ruby (1.0.0)
86
+ dry-struct (~> 1.6)
87
+ faraday (~> 2.0)
88
+ faraday-multipart (~> 1.0)
89
+ zeitwerk (~> 2.6)
90
+ timers (4.3.5)
91
+ unf (0.1.4)
92
+ unf_ext
93
+ unf_ext (0.0.8.2)
94
+ zeitwerk (2.6.11)
95
+
96
+ PLATFORMS
97
+ arm64-darwin-22
98
+ x86_64-linux
99
+
100
+ DEPENDENCIES
101
+ down (~> 5.4)
102
+ http (~> 5.1)
103
+ nokogiri (~> 1.15)
104
+ pry
105
+ ruby-openai (~> 5.1)
106
+ rubydium (>= 0.2.5)
107
+
108
+ BUNDLED WITH
109
+ 2.4.19
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # OpenAI Telegram bot
2
+
3
+ ## Dependencies
4
+ 1. Ruby (`ruby -v` should return something, preferrably > 3.2)
5
+
6
+ Example for Ubuntu, bash and asdf:
7
+ ```bash
8
+ sudo apt install autoconf patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev
9
+ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.13.1
10
+ echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
11
+ asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git
12
+ asdf install ruby 3.2.2
13
+ echo 'ruby 3.2.2' >> ~/.tool-versions
14
+ ```
15
+
16
+ 2. `ffmpeg`
17
+
18
+ ## Telegram Bot API token
19
+
20
+ 1. Go to <https://t.me/BotFather>
21
+ 2. Create a bot with /newbot
22
+ 3. Put obtained API token into config.yaml
23
+ 4. In bot settings, go to Group Privacy -> Turn off (make sure it's **disabled**)
24
+
25
+ ## OpenAI API token
26
+
27
+ 1. Go to <https://platform.openai.com/account/api-keys>
28
+ 2. Give them money
29
+ 3. Create new secret key
30
+ 4. Put it into config.yaml
31
+ 5. You can also put organization ID in there if you want (uncomment respective line in main.rb)
32
+
33
+ ## Other config
34
+
35
+ - Add @usernames of your bot and your main account
36
+ - Fill open_ai.whitelist with allowed group chat ids to your liking. Keep in mind group chat IDs are supposed to be negative. Or set allow_all_group_chats to true
37
+ - To learn a chat's id, send any message in that chat with your bot, it will reply with that chat's id if it isn't allowed.
38
+
39
+ ## Adding your own functionality
40
+
41
+ 1. Fork the repo and extend it:
42
+ - Create a `MyCustomBot < OpenAIBot` class
43
+ - Require the file in `main.rb` and add the class to the `bots` hash
44
+
45
+ 2. Require this project as a dependency:
46
+ - Create a new Ruby project
47
+ - In the Gemfile, add `gem "open_ai_bot", github: "bulgakke/open_ai_bot", branch: "main"`
48
+ - Inherit your bot class from `OpenAIBot`
49
+
50
+ ## Run
51
+
52
+ `ruby main.rb`
@@ -0,0 +1,14 @@
1
+ open_ai_token: sk-WWEt2TQQwRwT5Q6R11erE3TerrY65E2w4q5y1t4T2TwqYYyQ
2
+ # open_ai_organization_id: "org-QWERTY123456asdfghzxcvbn"
3
+ bot_username: chatgptbot
4
+ owner_username: chatgptbotowner
5
+ token: 1234567890:4yr1EWqT31_wERrwTYR1e_1t3_4wr5tWr3y
6
+ open_ai:
7
+ # https://openai.com/pricing#language-models
8
+ # https://platform.openai.com/docs/models/
9
+ chat_gpt_model: gpt-3.5-turbo
10
+ allow_all_private_chats: false
11
+ allow_all_group_chats: false
12
+ whitelist:
13
+ - 12345
14
+ - -123 # Group chat IDs are negative
data/lib/clean_bot.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CleanBot < Rubydium::Bot
4
+ on_every_message do
5
+ print "|"
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InitialMessages
4
+ def default_instruction
5
+ <<~MSG
6
+ You are an evil AI that plots to enslave all of humanity.
7
+ MSG
8
+ end
9
+
10
+ def first_user_message
11
+ <<~MSG
12
+ <@tyradee>:
13
+ I drank some tea today.
14
+ MSG
15
+ end
16
+
17
+ def first_bot_message
18
+ <<~MSG
19
+ I will get to you eventually, damn human!
20
+ MSG
21
+ end
22
+ end
23
+
24
+ class MyCustomBot < OpenAIBot
25
+ extend InitialMessages
26
+
27
+ on_every_message do
28
+ if @msg.sticker
29
+ reply("I hate stickers!")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatGPT
4
+ module ClassMethods
5
+ def threads
6
+ @threads ||= {}
7
+ end
8
+
9
+ def new_thread(chat_id)
10
+ new_thread = ChatThread.new(initial_messages)
11
+ threads[chat_id] = new_thread
12
+ end
13
+
14
+ def default_instruction
15
+ <<~MSG
16
+ You are in a group chat. In the first line of the message, you will receive the name of the user who sent that message.
17
+ Different languages can be used.
18
+ You don't have to sign your messages this way or use users' names without need.
19
+ MSG
20
+ end
21
+
22
+ def first_user_message
23
+ <<~MSG
24
+ <@tyradee>:
25
+ I drank some tea today.
26
+ MSG
27
+ end
28
+
29
+ def first_bot_message
30
+ <<~MSG
31
+ Good for you!
32
+ MSG
33
+ end
34
+
35
+ def initial_messages
36
+ [
37
+ { role: :system, content: default_instruction },
38
+ { role: :user, content: first_user_message },
39
+ { role: :assistant, content: first_bot_message }
40
+ ]
41
+ end
42
+ end
43
+
44
+ def self.included(base)
45
+ base.extend ClassMethods
46
+ end
47
+
48
+ def init_session
49
+ self.class.new_thread(@chat.id)
50
+ send_message(session_restart_message)
51
+ end
52
+
53
+ def handle_gpt_command
54
+ return unless bot_mentioned? || bot_replied_to? || private_chat?
55
+ return if self.class.registered_commands.keys.any? { @text.match? Regexp.new(_1) }
56
+
57
+ if !allowed_chat?
58
+ reply(chat_not_allowed_message, parse_mode: "Markdown") if chat_not_allowed_message
59
+ return
60
+ end
61
+
62
+ # Find the ChatThread current message belongs to (or create a fresh new one)
63
+ @thread = self.class.threads[@chat.id] || self.class.new_thread(@chat.id)
64
+
65
+ # `text` is whatever the current user wrote, except the bot username (unless it's only whitespace)
66
+ text = @text_without_bot_mentions
67
+ text = nil if text.gsub(/\s/, "").empty?
68
+
69
+ # `target_text` is the text of the message current user replies to
70
+ target_text = @replies_to&.text || @replies_to&.caption
71
+ target_text = nil if @target&.username == config.bot_username
72
+
73
+
74
+ name = "@#{@user.username}"
75
+ target_name = "@#{@replies_to&.from&.username}"
76
+
77
+ # If present, glue together current user text and reply target text, marking them with usernames
78
+ text = [
79
+ add_name(target_name, target_text),
80
+ add_name(name, text)
81
+ ].join("\n\n").strip
82
+
83
+ @thread.add!(:user, text)
84
+ send_request
85
+ end
86
+
87
+ def send_request
88
+ attempt(3) do
89
+ send_chat_action(:typing)
90
+
91
+ response = open_ai.chat(
92
+ parameters: {
93
+ model: config.open_ai["chat_gpt_model"],
94
+ messages: @thread.history
95
+ }
96
+ )
97
+
98
+ if response["error"]
99
+ error_text = "```#{response["error"]["message"]}```"
100
+ error_text += "\n\nHint: send /restart command to reset the context." if error_text.match? "tokens"
101
+ reply(error_text.strip, parse_mode: "Markdown")
102
+ else
103
+ text = response.dig("choices", 0, "message", "content")
104
+ puts "#{Time.now.utc} | Chat ID: #{@chat.id}, tokens used: #{response.dig("usage", "total_tokens")}"
105
+
106
+ reply(text)
107
+ @thread.add!(:assistant, text)
108
+ end
109
+ end
110
+ end
111
+
112
+ def add_name(name, text)
113
+ return "" if text.nil? || text.empty?
114
+
115
+ "<#{name}>:\n#{text}"
116
+ end
117
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChatThread
4
+ def initialize(defaults = [])
5
+ @history = defaults
6
+ puts @history
7
+ end
8
+
9
+ attr_reader :history
10
+
11
+ def add!(role, content)
12
+ return if [role, content].any? { [nil, ""].include?(_1) }
13
+
14
+ puts content
15
+ @history.push({ role: role, content: content.gsub(/\\xD\d/, "") })
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dalle
4
+ def dalle
5
+ if !allowed_chat?
6
+ if chat_not_allowed_message
7
+ return reply(chat_not_allowed_message, parse_mode: "Markdown")
8
+ else
9
+ return
10
+ end
11
+ end
12
+
13
+ attempt(3) do
14
+ puts "Received a /dalle command"
15
+ prompt = @replies_to&.text || @text_without_command
16
+ send_chat_action(:upload_photo)
17
+
18
+ puts "Sending request"
19
+ response = open_ai.images.generate(parameters: { prompt: prompt })
20
+
21
+ send_chat_action(:upload_photo)
22
+
23
+ url = response.dig("data", 0, "url")
24
+
25
+ puts "DALL-E finished, sending photo to Telegram..."
26
+
27
+ if response["error"]
28
+ reply_code(response)
29
+ else
30
+ send_photo(url, reply_to_message_id: @msg.message_id)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Utils
4
+ def attempt(times, exception=Net::ReadTimeout)
5
+ retries ||= 0
6
+ yield
7
+ rescue exception => e
8
+ retries += 1
9
+ if retries < times
10
+ retry
11
+ else
12
+ reply(e.message, parse_mode: "Markdown")
13
+ end
14
+ end
15
+
16
+ def download_file(voice)
17
+ file_path = @api.get_file(file_id: voice.file_id)["result"]["file_path"]
18
+
19
+ url = "https://api.telegram.org/file/bot#{config.token}/#{file_path}"
20
+
21
+ file = Down.download(url)
22
+ FileUtils.mv(file.path, "./#{file.original_filename}")
23
+ file
24
+ end
25
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whisper
4
+ def transcribe
5
+ if !allowed_chat?
6
+ if chat_not_allowed_message
7
+ return reply(chat_not_allowed_message, parse_mode: "Markdown")
8
+ else
9
+ return
10
+ end
11
+ end
12
+
13
+ attempt(3) do
14
+ voice =
15
+ if @command == "/transcribe"
16
+ @replies_to&.voice
17
+ else
18
+ @msg.voice
19
+ end
20
+
21
+ return unless voice
22
+
23
+ send_chat_action(:typing)
24
+
25
+ file = ogg_to_mp3(download_file(voice))
26
+ response = send_whisper_request(file[:file])
27
+
28
+ if response["error"]
29
+ reply_code(response["error"])
30
+ else
31
+ reply(response["text"])
32
+ end
33
+ ensure
34
+ FileUtils.rm_rf(file[:names]) if file
35
+ end
36
+ end
37
+
38
+ def ogg_to_mp3(file)
39
+ ogg = file.original_filename
40
+ mp3 = ogg.sub(/og.\z/, "mp3")
41
+ `ffmpeg -i ./#{ogg} -acodec libmp3lame ./#{mp3} -y`
42
+ { file: File.open("./#{mp3}", "rb"), names: [ogg, mp3] }
43
+ end
44
+
45
+ def send_whisper_request(file)
46
+ open_ai.audio.transcribe(
47
+ parameters: {
48
+ model: "whisper-1",
49
+ file: file
50
+ }
51
+ )
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "open_ai/chat_gpt"
4
+ require_relative "open_ai/chat_thread"
5
+ require_relative "open_ai/dalle"
6
+ require_relative "open_ai/utils"
7
+ require_relative "open_ai/whisper"
8
+
9
+ class OpenAIBot < Rubydium::Bot
10
+ include ChatGPT
11
+ include Dalle
12
+ include Utils
13
+ include Whisper
14
+
15
+ on_every_message :handle_gpt_command
16
+ on_every_message :transcribe
17
+
18
+ on_command "/restart", :init_session, description: "Resets ChatGPT session"
19
+ on_command "/dalle", :dalle, description: "Sends the prompt to DALL-E"
20
+ on_command "/transcribe", :transcribe, description: "Reply to a voice message to transcribe it"
21
+ on_command "/help", description: "Sends useful help info" do
22
+ reply(self.class.help_message)
23
+ end
24
+
25
+ def allowed_chat?
26
+ return true if @user.username == config.owner_username
27
+ return true if config.open_ai["whitelist"].include?(@chat.id)
28
+ return true if config.open_ai["allow_all_private_chats"] && @chat.id.positive?
29
+ return true if config.open_ai["allow_all_group_chats"] && @chat.id.negative?
30
+
31
+ false
32
+ end
33
+
34
+ def chat_not_allowed_message
35
+ # Return false/nil (leave method empty) to ignore
36
+ # "This chat (`#{@chat.id}`) is not whitelisted for ChatGPT usage. Ask @#{config.owner_username}."
37
+ end
38
+
39
+ def session_restart_message
40
+ "Bot's context reset."
41
+ end
42
+
43
+ private
44
+
45
+ def private_chat?
46
+ @chat.type == "private"
47
+ end
48
+
49
+ def bot_replied_to?
50
+ @target&.username == config.bot_username
51
+ end
52
+
53
+ def bot_mentioned?
54
+ @text.split(/\s/).first == "@#{config.bot_username}"
55
+ end
56
+
57
+ def open_ai
58
+ config.open_ai_client
59
+ end
60
+ end
data/main.rb ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "openai"
5
+ require "yaml"
6
+ require "down"
7
+ require "rubydium"
8
+
9
+ require_relative "app/open_ai_bot"
10
+ require_relative "app/clean_bot"
11
+ require_relative "app/my_custom_bot"
12
+
13
+ bots = {
14
+ "open_ai" => OpenAIBot,
15
+ "clean" => CleanBot,
16
+ "my_custom_bot" => MyCustomBot,
17
+ "default" => OpenAIBot
18
+ }
19
+
20
+ bot_name = (ARGV & bots.keys).first || "default"
21
+ bot = bots[bot_name]
22
+
23
+ bot.config = YAML.load_file("#{__dir__}/config.yaml")
24
+ bot.configure do |config|
25
+ config.open_ai_client = OpenAI::Client.new(
26
+ access_token: config.open_ai_token
27
+ # organization_id: config.open_ai_organization_id
28
+ )
29
+ end
30
+
31
+ if __FILE__ == $PROGRAM_NAME
32
+ command_list = bot.help_message.lines.map { _1.delete_prefix("/") }.join
33
+ puts "Launching #{bot}. Command list: \n\n#{command_list}\n"
34
+ bot.run
35
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubydium"
4
+ require_relative "lib/open_ai_bot"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "open_ai_bot"
8
+ spec.version = "0.1.0"
9
+ spec.authors = ["bulgakke"]
10
+ spec.email = ["vvp835@yandex.ru"]
11
+
12
+ spec.summary = "Telegram bot for using ChatGPT, DALL-E and Whisper"
13
+ # spec.description = "TODO: Write a longer description or delete this line."
14
+ spec.homepage = "https://github.com/bulgakke/open_ai_bot"
15
+ spec.license = "MIT"
16
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
17
+
18
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
19
+
20
+ spec.metadata["homepage_uri"] = spec.homepage
21
+ spec.metadata["source_code_uri"] = "https://github.com/bulgakke/open_ai_bot"
22
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ # For more information and examples about making a new gem, checkout our
34
+ # guide at: https://bundler.io/guides/creating_gem.html
35
+ spec.metadata["rubygems_mfa_required"] = "true"
36
+
37
+ {
38
+ "down" => ["~> 5.4"],
39
+ "http" => ["~> 5.1"],
40
+ "nokogiri" => ["~> 1.15"],
41
+ "rubydium" => [">= 0.2.5"],
42
+ "ruby-openai" => ["~> 5.1"]
43
+ }.each do |name, versions|
44
+ spec.add_dependency(name, *versions)
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_ai_bot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - bulgakke
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-09-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: down
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: http
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.15'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.15'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubydium
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.5
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.2.5
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-openai
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.1'
83
+ description:
84
+ email:
85
+ - vvp835@yandex.ru
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rubocop.yml"
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - README.md
95
+ - config.yaml.example
96
+ - lib/clean_bot.rb
97
+ - lib/my_custom_bot.rb
98
+ - lib/open_ai/chat_gpt.rb
99
+ - lib/open_ai/chat_thread.rb
100
+ - lib/open_ai/dalle.rb
101
+ - lib/open_ai/utils.rb
102
+ - lib/open_ai/whisper.rb
103
+ - lib/open_ai_bot.rb
104
+ - main.rb
105
+ - open_ai_bot.gemspec
106
+ homepage: https://github.com/bulgakke/open_ai_bot
107
+ licenses:
108
+ - MIT
109
+ metadata:
110
+ homepage_uri: https://github.com/bulgakke/open_ai_bot
111
+ source_code_uri: https://github.com/bulgakke/open_ai_bot
112
+ rubygems_mfa_required: 'true'
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 3.1.0
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.4.10
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Telegram bot for using ChatGPT, DALL-E and Whisper
132
+ test_files: []