rodbot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +653 -0
- data/exe/rodbot +7 -0
- data/lib/roda/plugins/rodbot.rb +36 -0
- data/lib/rodbot/async.rb +45 -0
- data/lib/rodbot/cli/command.rb +25 -0
- data/lib/rodbot/cli/commands/console.rb +23 -0
- data/lib/rodbot/cli/commands/credentials.rb +19 -0
- data/lib/rodbot/cli/commands/deploy.rb +20 -0
- data/lib/rodbot/cli/commands/new.rb +21 -0
- data/lib/rodbot/cli/commands/simulator.rb +17 -0
- data/lib/rodbot/cli/commands/start.rb +26 -0
- data/lib/rodbot/cli/commands/stop.rb +15 -0
- data/lib/rodbot/cli/commands/version.rb +15 -0
- data/lib/rodbot/cli/commands.rb +18 -0
- data/lib/rodbot/cli.rb +9 -0
- data/lib/rodbot/config.rb +157 -0
- data/lib/rodbot/constants.rb +13 -0
- data/lib/rodbot/db/hash.rb +71 -0
- data/lib/rodbot/db/redis.rb +61 -0
- data/lib/rodbot/db.rb +91 -0
- data/lib/rodbot/dispatcher.rb +125 -0
- data/lib/rodbot/env.rb +48 -0
- data/lib/rodbot/error.rb +19 -0
- data/lib/rodbot/generator.rb +108 -0
- data/lib/rodbot/log.rb +67 -0
- data/lib/rodbot/memoize.rb +86 -0
- data/lib/rodbot/plugins/github_webhook/README.github_webhook.md +42 -0
- data/lib/rodbot/plugins/github_webhook/app.rb +46 -0
- data/lib/rodbot/plugins/gitlab_webhook/README.gitlab_webhook.md +40 -0
- data/lib/rodbot/plugins/gitlab_webhook/app.rb +40 -0
- data/lib/rodbot/plugins/hal/README.hal.md +15 -0
- data/lib/rodbot/plugins/hal/app.rb +22 -0
- data/lib/rodbot/plugins/matrix/README.matrix.md +42 -0
- data/lib/rodbot/plugins/matrix/relay.rb +113 -0
- data/lib/rodbot/plugins/otp/README.otp.md +82 -0
- data/lib/rodbot/plugins/otp/app.rb +47 -0
- data/lib/rodbot/plugins/word_of_the_day/README.word_of_the_day.md +13 -0
- data/lib/rodbot/plugins/word_of_the_day/schedule.rb +51 -0
- data/lib/rodbot/plugins.rb +81 -0
- data/lib/rodbot/rack.rb +50 -0
- data/lib/rodbot/refinements.rb +118 -0
- data/lib/rodbot/relay.rb +104 -0
- data/lib/rodbot/services/app.rb +72 -0
- data/lib/rodbot/services/relay.rb +37 -0
- data/lib/rodbot/services/schedule.rb +29 -0
- data/lib/rodbot/services.rb +32 -0
- data/lib/rodbot/simulator.rb +60 -0
- data/lib/rodbot/version.rb +5 -0
- data/lib/rodbot.rb +60 -0
- data/lib/templates/deploy/docker/compose.yaml.gerb +37 -0
- data/lib/templates/deploy/docker-split/compose.yaml.gerb +52 -0
- data/lib/templates/deploy/procfile/Procfile.gerb +1 -0
- data/lib/templates/deploy/procfile-split/Procfile.gerb +5 -0
- data/lib/templates/deploy/render/render.yaml.gerb +0 -0
- data/lib/templates/deploy/render-split/render.yaml.gerb +0 -0
- data/lib/templates/new/LICENSE.txt +22 -0
- data/lib/templates/new/README.md +4 -0
- data/lib/templates/new/app/app.rb +11 -0
- data/lib/templates/new/app/routes/help.rb +19 -0
- data/lib/templates/new/app/views/layout.erb +12 -0
- data/lib/templates/new/app/views/root.erb +5 -0
- data/lib/templates/new/config/rodbot.rb +8 -0
- data/lib/templates/new/config/schedule.rb +5 -0
- data/lib/templates/new/config.ru +3 -0
- data/lib/templates/new/gems.locked +104 -0
- data/lib/templates/new/gems.rb +15 -0
- data/lib/templates/new/guardfile.rb +9 -0
- data/lib/templates/new/public/assets/images/rodbot.avif +0 -0
- data/lib/templates/new/public/assets/stylesheets/base.css +18 -0
- data/lib/templates/new/rakefile.rb +28 -0
- data.tar.gz.sig +0 -0
- metadata +510 -0
- metadata.gz.sig +3 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
require 'puma'
|
5
|
+
require 'roda'
|
6
|
+
|
7
|
+
module Rodbot
|
8
|
+
class Services
|
9
|
+
class App
|
10
|
+
include Rodbot::Memoize
|
11
|
+
|
12
|
+
class << self
|
13
|
+
include Rodbot::Memoize
|
14
|
+
|
15
|
+
# URL (including port) to reach the app service locally
|
16
|
+
#
|
17
|
+
# @return [String] URL
|
18
|
+
memoize def url
|
19
|
+
[
|
20
|
+
(ENV['RODBOT_APP_URL'] || 'http://localhost'),
|
21
|
+
Rodbot.config(:port)
|
22
|
+
].join(':')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def tasks(**)
|
27
|
+
puts "Starting app service on http://#{bind.join(':')}"
|
28
|
+
[method(:run)]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def run
|
34
|
+
Dir.chdir(Rodbot.env.root)
|
35
|
+
Puma::Server.new(app, nil, options).tap do |server|
|
36
|
+
server.add_tcp_listener(*bind)
|
37
|
+
server.app = ::Rack::CommonLogger.new(app, logger)
|
38
|
+
end.run.join
|
39
|
+
end
|
40
|
+
|
41
|
+
memoize def bind
|
42
|
+
[
|
43
|
+
(ENV['RODBOT_APP_HOST'] || 'localhost'),
|
44
|
+
Rodbot.config(:port)
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
memoize def app
|
49
|
+
::Rack::Builder.parse_file(Rodbot.env.root.join('config.ru').to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
def options
|
53
|
+
{
|
54
|
+
lowlevel_error_handler: method(:lowlevel_error_handler),
|
55
|
+
log_writer: Puma::LogWriter.null,
|
56
|
+
min_threads: Rodbot.config(:app, :threads).min,
|
57
|
+
max_threads: Rodbot.config(:app, :threads).max
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
memoize def logger
|
62
|
+
Rodbot::Log.logger('app')
|
63
|
+
end
|
64
|
+
|
65
|
+
def lowlevel_error_handler(error)
|
66
|
+
logger.error("#{error.message}: #{error.backtrace.first}")
|
67
|
+
[500, {}, ['Oops!']]
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
using Rodbot::Refinements
|
4
|
+
|
5
|
+
module Rodbot
|
6
|
+
class Services
|
7
|
+
class Relay
|
8
|
+
|
9
|
+
class << self
|
10
|
+
include Rodbot::Memoize
|
11
|
+
|
12
|
+
# URL (including port) to reach the given relay service locally
|
13
|
+
#
|
14
|
+
# @param name [Symbol] relay service
|
15
|
+
# @return [String] URL
|
16
|
+
memoize def url(name)
|
17
|
+
[
|
18
|
+
(ENV["RODBOT_RELAY_URL_#{name.upcase}"] || 'tcp://localhost'),
|
19
|
+
Rodbot.config(:port) + 1 + Rodbot.config(:plugin).keys.index(name)
|
20
|
+
].join(':')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def tasks(only: nil)
|
25
|
+
Rodbot.plugins.extend_relay
|
26
|
+
extensions = Rodbot.plugins.extensions[:relay]
|
27
|
+
extensions.select! { _1 == only.to_sym } if only
|
28
|
+
fail Rodbot::RelayError, "no matching relay plugin configured" if extensions.none?
|
29
|
+
extensions.map do |name, path|
|
30
|
+
puts "Starting relay service extension #{name} on #{self.class.url(name)}"
|
31
|
+
path.constantize.new.loops
|
32
|
+
end.flatten
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'clockwork'
|
4
|
+
require 'active_support/time'
|
5
|
+
|
6
|
+
module Rodbot
|
7
|
+
class Services
|
8
|
+
class Schedule
|
9
|
+
|
10
|
+
def tasks(**)
|
11
|
+
puts "Starting schedule service"
|
12
|
+
[method(:run)]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def run
|
18
|
+
Clockwork.instance_eval do
|
19
|
+
configure { _1[:logger] = Rodbot::Log.logger('schedule') }
|
20
|
+
handler { Rodbot::Async.perform(&_1) }
|
21
|
+
end
|
22
|
+
Rodbot.plugins.extend_schedule
|
23
|
+
require Rodbot.env.root.join('config', 'schedule')
|
24
|
+
Clockwork.run
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
using Rodbot::Refinements
|
4
|
+
|
5
|
+
module Rodbot
|
6
|
+
|
7
|
+
# Foundation to run app, relay and schedule services
|
8
|
+
class Services
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegator :@dispatcher, :run, :run
|
12
|
+
def_delegator :@dispatcher, :interrupt, :interrupt
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@dispatcher = Rodbot::Dispatcher.new('rodbot')
|
16
|
+
end
|
17
|
+
|
18
|
+
def exist?(service)
|
19
|
+
Rodbot::SERVICES.include? service.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def register(service, extension: nil)
|
23
|
+
fail(Rodbot::ServiceError, "unknown service #{service}") unless exist? service
|
24
|
+
tasks = "rodbot/services/#{service}".constantize.new.tasks(only: extension)
|
25
|
+
tasks.each_with_index do |task, index|
|
26
|
+
name = [service, (index if tasks.count > 1)].compact.join('-')
|
27
|
+
@dispatcher.register(name, &task)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'readline'
|
4
|
+
require 'httparty'
|
5
|
+
require 'pastel'
|
6
|
+
require 'tty-markdown'
|
7
|
+
|
8
|
+
using Rodbot::Refinements
|
9
|
+
|
10
|
+
module Rodbot
|
11
|
+
|
12
|
+
# Simulate a chat client
|
13
|
+
class Simulator
|
14
|
+
|
15
|
+
# @param sender [String] sender to mimick
|
16
|
+
# @param raw [Boolean] whether to display raw Markdown
|
17
|
+
def initialize(sender, raw: false)
|
18
|
+
@sender, @raw = sender, raw
|
19
|
+
@pastel = Pastel.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
puts nil, "Talking to app on #{Rodbot::Services::App.url} as sender #{@pastel.inverse(@sender)}."
|
24
|
+
puts 'Type commands beginning with "!" or empty line to exit.', nil
|
25
|
+
while (line = Readline.readline("rodbot> ", true)) && !line.empty?
|
26
|
+
puts nil, reply_to(line), nil
|
27
|
+
end
|
28
|
+
puts
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def reply_to(message)
|
34
|
+
return "(no command given)" unless message.match?(/^!/)
|
35
|
+
command, argument = message[1..].split(/\s+/, 2)
|
36
|
+
body = begin
|
37
|
+
response = Rodbot.request(command, query: { argument: argument })
|
38
|
+
case response.code
|
39
|
+
when 200 then response.body
|
40
|
+
when 404 then "[[SENDER]] I've never heard of `!#{command}`, try `!help` instead. 🤔"
|
41
|
+
else fail
|
42
|
+
end
|
43
|
+
rescue
|
44
|
+
"[[SENDER]] I'm having trouble talking to the app. 💣"
|
45
|
+
end
|
46
|
+
text_for body.psub(placeholders)
|
47
|
+
end
|
48
|
+
|
49
|
+
def placeholders
|
50
|
+
{
|
51
|
+
sender: @pastel.inverse(@sender)
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def text_for(markdown)
|
56
|
+
@raw ? markdown : TTY::Markdown.parse(markdown, mode: 16).strip
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/lib/rodbot.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
require 'zeitwerk'
|
6
|
+
require 'dry/credentials'
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
loader = Zeitwerk::Loader.for_gem
|
10
|
+
loader.inflector.inflect 'cli' => 'CLI'
|
11
|
+
%w(rodbot/plugins roda templates).each { loader.ignore "#{__dir__}/#{_1}" }
|
12
|
+
loader.setup
|
13
|
+
|
14
|
+
# Rodbot foundation
|
15
|
+
#
|
16
|
+
# Once the Rodbot gem has been required, use +boot+ to spin up the foundation:
|
17
|
+
#
|
18
|
+
# Rodbot.boot # current directory is root directory
|
19
|
+
# Rodbot.boot(root: '/path/to/root') # explicit root directory
|
20
|
+
#
|
21
|
+
# This gives you access to the following shortcuts (in order of loading):
|
22
|
+
#
|
23
|
+
# * +Rodbot.env+ -> {Rodbot::Env}
|
24
|
+
# * +Rodbot.credentials+ -> {Dry::Credentials}
|
25
|
+
# * +Rodbot.config+ -> {Rodbot::Config#config}
|
26
|
+
# * +Rodbot.plugins+ -> {Rodbot::Plugins}
|
27
|
+
# * +Rodbot.db+ -> {Rodbot::Db#db}
|
28
|
+
# * +Rodbot.log+ -> {Rodbot::Log#log}
|
29
|
+
# * +Rodbot.request+ -> {Rodbot::Rack#request}
|
30
|
+
# * +Rodbot.say+ -> {Rodbot::Relay#say}
|
31
|
+
module Rodbot
|
32
|
+
include Rodbot::Constants
|
33
|
+
extend Dry::Credentials
|
34
|
+
|
35
|
+
class << self
|
36
|
+
extend Forwardable
|
37
|
+
|
38
|
+
attr_reader :env
|
39
|
+
def_delegator :@config, :config
|
40
|
+
def_delegator :@plugins, :itself, :plugins
|
41
|
+
def_delegator :@db, :itself, :db
|
42
|
+
def_delegator :@log, :log
|
43
|
+
def_delegator 'Rodbot::Rack', :request
|
44
|
+
def_delegator 'Rodbot::Relay', :say
|
45
|
+
|
46
|
+
def boot(root: nil)
|
47
|
+
@env = Rodbot::Env.new(root: root)
|
48
|
+
credentials do
|
49
|
+
env Rodbot.env.current
|
50
|
+
dir ENV['RODBOT_CREDENTIALS_DIR'] || Rodbot.env.root.join('config', 'credentials')
|
51
|
+
end
|
52
|
+
@config = Rodbot::Config.new(Rodbot.env.root.join('config', 'rodbot.rb').read)
|
53
|
+
@plugins = Rodbot::Plugins.new
|
54
|
+
@db = (db = @config.config(:db)) && Rodbot::Db.new(db)
|
55
|
+
@log = Rodbot::Log.new
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
loader.eager_load_namespace(Rodbot::Error)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
version: "3.9"
|
2
|
+
|
3
|
+
x-defaults: &defaults
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
dockerfile_inline: |
|
7
|
+
FROM ruby:3.2-alpine
|
8
|
+
RUN apk update && apk --no-cache add build-base
|
9
|
+
ENV RODBOT_ENV="production"
|
10
|
+
ENV RACK_ENV="production"
|
11
|
+
ENV RACK_ROOT="/var/www"
|
12
|
+
ENV TZ="[%= timezone %]"
|
13
|
+
RUN mkdir -p /var/www
|
14
|
+
WORKDIR /var/www
|
15
|
+
COPY . .
|
16
|
+
COPY .bundle /usr/local/bundle
|
17
|
+
RUN bundle config set without "development test" && \
|
18
|
+
bundle install --jobs 20 --retry 5
|
19
|
+
environment:
|
20
|
+
- RODBOT_APP_HOST=0.0.0.0
|
21
|
+
- PRODUCTION_CREDENTIALS_KEY
|
22
|
+
restart: "unless-stopped"
|
23
|
+
|
24
|
+
services:
|
25
|
+
rodbot:
|
26
|
+
<<: *defaults
|
27
|
+
command: "sh -c 'bundle exec rodbot start && sleep infinity'"
|
28
|
+
ports:
|
29
|
+
- "[%= Rodbot.config(:app, :port) %]"
|
30
|
+
|
31
|
+
networks:
|
32
|
+
default:
|
33
|
+
driver: "bridge"
|
34
|
+
ipam:
|
35
|
+
driver: "default"
|
36
|
+
config:
|
37
|
+
- subnet: "172.16.72.0/24"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
version: "3.9"
|
2
|
+
|
3
|
+
x-defaults: &defaults
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
dockerfile_inline: |
|
7
|
+
FROM ruby:3.2-alpine
|
8
|
+
RUN apk update && apk --no-cache add build-base
|
9
|
+
ENV RODBOT_ENV="production"
|
10
|
+
ENV RACK_ENV="production"
|
11
|
+
ENV RACK_ROOT="/var/www"
|
12
|
+
ENV TZ="[%= timezone %]"
|
13
|
+
RUN mkdir -p /var/www
|
14
|
+
WORKDIR /var/www
|
15
|
+
COPY . .
|
16
|
+
COPY .bundle /usr/local/bundle
|
17
|
+
RUN bundle config set without "development test" && \
|
18
|
+
bundle install --jobs 20 --retry 5
|
19
|
+
environment:
|
20
|
+
- RODBOT_APP_HOST=0.0.0.0
|
21
|
+
- RODBOT_APP_URL=http://app
|
22
|
+
- RODBOT_RELAY_HOST=0.0.0.0
|
23
|
+
[% relay_extensions.each do |name, _| -%]
|
24
|
+
- RODBOT_RELAY_URL_[%= name.upcase %]=tcp://relay-[%= name %]
|
25
|
+
[% end -%]
|
26
|
+
- PRODUCTION_CREDENTIALS_KEY
|
27
|
+
restart: "unless-stopped"
|
28
|
+
|
29
|
+
services:
|
30
|
+
app:
|
31
|
+
<<: *defaults
|
32
|
+
command: "bundle exec rodbot start app"
|
33
|
+
ports:
|
34
|
+
- "[%= Rodbot.config(:app, :port) %]"
|
35
|
+
schedule:
|
36
|
+
<<: *defaults
|
37
|
+
command: "bundle exec rodbot start schedule"
|
38
|
+
[% relay_extensions.each do |name, port| -%]
|
39
|
+
relay-[%= name %]:
|
40
|
+
<<: *defaults
|
41
|
+
command: "bundle exec rodbot start relay [%= name %]"
|
42
|
+
expose:
|
43
|
+
- [%= port %]
|
44
|
+
[% end -%]
|
45
|
+
|
46
|
+
networks:
|
47
|
+
default:
|
48
|
+
driver: "bridge"
|
49
|
+
ipam:
|
50
|
+
driver: "default"
|
51
|
+
config:
|
52
|
+
- subnet: "172.16.72.0/24"
|
@@ -0,0 +1 @@
|
|
1
|
+
rodbot: bundle exec rodbot start
|
File without changes
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Sven Schwyn
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Routes
|
2
|
+
class Help < App
|
3
|
+
|
4
|
+
route do |r|
|
5
|
+
|
6
|
+
# GET /help
|
7
|
+
r.root do
|
8
|
+
response['Content-Type'] = 'text/markdown; charset=utf-8'
|
9
|
+
<<~END
|
10
|
+
[[SENDER]] I'm Rodbot, what can I do for you today?
|
11
|
+
|
12
|
+
* `!ping` – check whether I'm listening
|
13
|
+
END
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<title>title</title>
|
7
|
+
<link rel="stylesheet" href="/assets/stylesheets/base.css">
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<%= yield %>
|
11
|
+
</body>
|
12
|
+
</html>
|
@@ -0,0 +1,104 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../../..
|
3
|
+
specs:
|
4
|
+
rodbot (0.1.0.pre1)
|
5
|
+
clockwork (~> 3)
|
6
|
+
debug
|
7
|
+
dry-cli (~> 1)
|
8
|
+
dry-credentials (~> 0)
|
9
|
+
httparty (~> 0)
|
10
|
+
kramdown (~> 2)
|
11
|
+
kramdown-parser-gfm (~> 1)
|
12
|
+
matrix_sdk (~> 2)
|
13
|
+
pastel (~> 0)
|
14
|
+
puma (~> 6, >= 6.2)
|
15
|
+
roda (~> 3)
|
16
|
+
rotp (~> 6)
|
17
|
+
sucker_punch (~> 3)
|
18
|
+
tilt (~> 2)
|
19
|
+
tty-markdown (~> 0)
|
20
|
+
zeitwerk (~> 2)
|
21
|
+
|
22
|
+
GEM
|
23
|
+
remote: https://rubygems.org/
|
24
|
+
specs:
|
25
|
+
activesupport (7.0.4.3)
|
26
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
27
|
+
i18n (>= 1.6, < 2)
|
28
|
+
minitest (>= 5.1)
|
29
|
+
tzinfo (~> 2.0)
|
30
|
+
clockwork (3.0.2)
|
31
|
+
activesupport
|
32
|
+
tzinfo
|
33
|
+
concurrent-ruby (1.2.2)
|
34
|
+
debug (1.8.0)
|
35
|
+
irb (>= 1.5.0)
|
36
|
+
reline (>= 0.3.1)
|
37
|
+
dry-cli (1.0.0)
|
38
|
+
dry-credentials (0.1.0)
|
39
|
+
httparty (0.21.0)
|
40
|
+
mini_mime (>= 1.0.0)
|
41
|
+
multi_xml (>= 0.5.2)
|
42
|
+
i18n (1.12.0)
|
43
|
+
concurrent-ruby (~> 1.0)
|
44
|
+
io-console (0.6.0)
|
45
|
+
irb (1.6.4)
|
46
|
+
reline (>= 0.3.0)
|
47
|
+
kramdown (2.4.0)
|
48
|
+
rexml
|
49
|
+
kramdown-parser-gfm (1.1.0)
|
50
|
+
kramdown (~> 2.0)
|
51
|
+
little-plugger (1.1.4)
|
52
|
+
logging (2.3.1)
|
53
|
+
little-plugger (~> 1.1)
|
54
|
+
multi_json (~> 1.14)
|
55
|
+
matrix_sdk (2.8.0)
|
56
|
+
logging (~> 2)
|
57
|
+
mini_mime (1.1.2)
|
58
|
+
minitest (5.18.0)
|
59
|
+
multi_json (1.15.0)
|
60
|
+
multi_xml (0.6.0)
|
61
|
+
nio4r (2.5.8)
|
62
|
+
pastel (0.8.0)
|
63
|
+
tty-color (~> 0.5)
|
64
|
+
puma (6.2.0)
|
65
|
+
nio4r (~> 2.0)
|
66
|
+
rack (3.0.7)
|
67
|
+
reline (0.3.3)
|
68
|
+
io-console (~> 0.5)
|
69
|
+
rexml (3.2.5)
|
70
|
+
roda (3.66.0)
|
71
|
+
rack
|
72
|
+
rotp (6.2.2)
|
73
|
+
rouge (4.1.0)
|
74
|
+
strings (0.2.1)
|
75
|
+
strings-ansi (~> 0.2)
|
76
|
+
unicode-display_width (>= 1.5, < 3.0)
|
77
|
+
unicode_utils (~> 1.4)
|
78
|
+
strings-ansi (0.2.0)
|
79
|
+
sucker_punch (3.1.0)
|
80
|
+
concurrent-ruby (~> 1.0)
|
81
|
+
tilt (2.1.0)
|
82
|
+
tty-color (0.6.0)
|
83
|
+
tty-markdown (0.7.2)
|
84
|
+
kramdown (>= 1.16.2, < 3.0)
|
85
|
+
pastel (~> 0.8)
|
86
|
+
rouge (>= 3.14, < 5.0)
|
87
|
+
strings (~> 0.2.0)
|
88
|
+
tty-color (~> 0.5)
|
89
|
+
tty-screen (~> 0.8)
|
90
|
+
tty-screen (0.8.1)
|
91
|
+
tzinfo (2.0.6)
|
92
|
+
concurrent-ruby (~> 1.0)
|
93
|
+
unicode-display_width (2.4.2)
|
94
|
+
unicode_utils (1.4.0)
|
95
|
+
zeitwerk (2.6.7)
|
96
|
+
|
97
|
+
PLATFORMS
|
98
|
+
arm64-darwin-22
|
99
|
+
|
100
|
+
DEPENDENCIES
|
101
|
+
rodbot!
|
102
|
+
|
103
|
+
BUNDLED WITH
|
104
|
+
2.4.12
|
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'rodbot', '~> 0'
|
4
|
+
|
5
|
+
group :matrix, optional: true do
|
6
|
+
gem 'matrix_sdk', '~> 2'
|
7
|
+
end
|
8
|
+
|
9
|
+
group :redis, optional: true do
|
10
|
+
gem 'redis', '~> 5'
|
11
|
+
end
|
12
|
+
|
13
|
+
group :otp, optional: true do
|
14
|
+
gem 'rotp', '~> 6'
|
15
|
+
end
|
Binary file
|
@@ -0,0 +1,18 @@
|
|
1
|
+
main {
|
2
|
+
display: flex;
|
3
|
+
justify-content: center;
|
4
|
+
align-items: center;
|
5
|
+
height: 100vh;
|
6
|
+
}
|
7
|
+
|
8
|
+
a {
|
9
|
+
display: flex;
|
10
|
+
justify-content: center;
|
11
|
+
align-items: center;
|
12
|
+
width: 50%;
|
13
|
+
}
|
14
|
+
|
15
|
+
a img {
|
16
|
+
max-width: 100%;
|
17
|
+
height: auto;
|
18
|
+
}
|