open_ai_bot 0.1.0

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.
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: []