mirror of
https://github.com/discourse/discourse.git
synced 2024-12-14 19:03:42 +08:00
d9a02d1336
This reverts commit20780a1eee
. * SECURITY: re-adds accidentally reverted commit: 03d26cd6: ensure embed_url contains valid http(s) uri * when the merge commite62a85cf
was reverted, git chose the2660c2e2
parent to land on instead of the03d26cd6
parent (which contains security fixes)
281 lines
8.4 KiB
Ruby
281 lines
8.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# name: discourse-narrative-bot
|
|
# about: Introduces staff to Discourse
|
|
# version: 1.0
|
|
# authors: Nick Sahler, Alan Tan
|
|
# url: https://github.com/discourse/discourse/tree/master/plugins/discourse-narrative-bot
|
|
|
|
enabled_site_setting :discourse_narrative_bot_enabled
|
|
hide_plugin if self.respond_to?(:hide_plugin)
|
|
|
|
if Rails.env == "development"
|
|
# workaround, teach reloader to reload jobs
|
|
# if we do not do this then
|
|
#
|
|
# 1. on reload rails goes and undefines Jobs::Base
|
|
# 2. as a side effect this undefines Jobs::BotInput
|
|
# 3. we have a post_edited hook that queues a job for bot input
|
|
# 4. if you are not running sidekiq in dev every time you save a post it will trigger it
|
|
# 5. but the constant can not be autoloaded
|
|
Rails.configuration.autoload_paths << File.expand_path('../autoload', __FILE__)
|
|
end
|
|
|
|
require_relative 'lib/discourse_narrative_bot/welcome_post_type_site_setting.rb'
|
|
|
|
after_initialize do
|
|
SeedFu.fixture_paths << Rails.root.join("plugins", "discourse-narrative-bot", "db", "fixtures").to_s
|
|
|
|
Mime::Type.register "image/svg+xml", :svg
|
|
|
|
[
|
|
'../autoload/jobs/bot_input.rb',
|
|
'../autoload/jobs/narrative_timeout.rb',
|
|
'../autoload/jobs/narrative_init.rb',
|
|
'../autoload/jobs/send_default_welcome_message.rb',
|
|
'../autoload/jobs/send_advanced_tutorial_message.rb',
|
|
'../autoload/jobs/onceoff/grant_badges.rb',
|
|
'../autoload/jobs/onceoff/remap_old_bot_images.rb',
|
|
'../lib/discourse_narrative_bot/actions.rb',
|
|
'../lib/discourse_narrative_bot/base.rb',
|
|
'../lib/discourse_narrative_bot/new_user_narrative.rb',
|
|
'../lib/discourse_narrative_bot/advanced_user_narrative.rb',
|
|
'../lib/discourse_narrative_bot/track_selector.rb',
|
|
'../lib/discourse_narrative_bot/certificate_generator.rb',
|
|
'../lib/discourse_narrative_bot/dice.rb',
|
|
'../lib/discourse_narrative_bot/quote_generator.rb',
|
|
'../lib/discourse_narrative_bot/magic_8_ball.rb',
|
|
'../lib/discourse_narrative_bot/welcome_post_type_site_setting.rb'
|
|
].each { |path| load File.expand_path(path, __FILE__) }
|
|
|
|
# Disable welcome message because that is what the bot is supposed to replace.
|
|
SiteSetting.send_welcome_message = false if SiteSetting.send_welcome_message
|
|
|
|
require_dependency 'plugin_store'
|
|
|
|
module ::DiscourseNarrativeBot
|
|
PLUGIN_NAME = "discourse-narrative-bot".freeze
|
|
BOT_USER_ID = -2
|
|
|
|
class Engine < ::Rails::Engine
|
|
engine_name PLUGIN_NAME
|
|
isolate_namespace DiscourseNarrativeBot
|
|
end
|
|
|
|
class Store
|
|
def self.set(key, value)
|
|
::PluginStore.set(PLUGIN_NAME, key, value)
|
|
end
|
|
|
|
def self.get(key)
|
|
::PluginStore.get(PLUGIN_NAME, key)
|
|
end
|
|
|
|
def self.remove(key)
|
|
::PluginStore.remove(PLUGIN_NAME, key)
|
|
end
|
|
end
|
|
|
|
class CertificatesController < ::ApplicationController
|
|
layout :false
|
|
skip_before_action :check_xhr
|
|
requires_login
|
|
|
|
def generate
|
|
immutable_for(24.hours)
|
|
|
|
%i[date user_id].each do |key|
|
|
raise Discourse::InvalidParameters.new("#{key} must be present") unless params[key]&.present?
|
|
end
|
|
|
|
rate_limiter = RateLimiter.new(current_user, 'svg_certificate', 3, 1.minute)
|
|
rate_limiter.performed! unless current_user.staff?
|
|
|
|
user = User.find_by(id: params[:user_id])
|
|
raise Discourse::NotFound if user.blank?
|
|
|
|
hijack do
|
|
avatar_data = fetch_avatar(user)
|
|
generator = CertificateGenerator.new(user, params[:date], avatar_data)
|
|
|
|
svg = params[:type] == 'advanced' ? generator.advanced_user_track : generator.new_user_track
|
|
|
|
respond_to do |format|
|
|
format.svg { render inline: svg }
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def fetch_avatar(user)
|
|
avatar_url = UrlHelper.absolute(Discourse.base_uri + user.avatar_template.gsub('{size}', '250'))
|
|
FileHelper.download(
|
|
avatar_url.to_s,
|
|
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
|
|
tmp_file_name: 'narrative-bot-avatar',
|
|
follow_redirect: true
|
|
)&.read
|
|
rescue OpenURI::HTTPError
|
|
# Ignore if fetching image returns a non 200 response
|
|
end
|
|
end
|
|
end
|
|
|
|
DiscourseNarrativeBot::Engine.routes.draw do
|
|
get "/certificate" => "certificates#generate", format: :svg
|
|
end
|
|
|
|
Discourse::Application.routes.append do
|
|
mount ::DiscourseNarrativeBot::Engine, at: "/discobot"
|
|
end
|
|
|
|
self.add_model_callback(User, :after_destroy) do
|
|
DiscourseNarrativeBot::Store.remove(self.id)
|
|
end
|
|
|
|
self.on(:user_created) do |user|
|
|
if SiteSetting.discourse_narrative_bot_welcome_post_delay == 0 && !user.staged
|
|
user.enqueue_bot_welcome_post
|
|
end
|
|
end
|
|
|
|
self.on(:user_first_logged_in) do |user|
|
|
if SiteSetting.discourse_narrative_bot_welcome_post_delay > 0
|
|
user.enqueue_bot_welcome_post
|
|
end
|
|
end
|
|
|
|
self.on(:user_unstaged) do |user|
|
|
user.enqueue_bot_welcome_post
|
|
end
|
|
|
|
self.add_to_class(:user, :enqueue_bot_welcome_post) do
|
|
return if SiteSetting.disable_discourse_narrative_bot_welcome_post
|
|
|
|
delay = SiteSetting.discourse_narrative_bot_welcome_post_delay
|
|
|
|
case SiteSetting.discourse_narrative_bot_welcome_post_type
|
|
when 'new_user_track'
|
|
if enqueue_narrative_bot_job?
|
|
Jobs.enqueue_in(delay, :narrative_init,
|
|
user_id: self.id,
|
|
klass: DiscourseNarrativeBot::NewUserNarrative.to_s
|
|
)
|
|
end
|
|
when 'welcome_message'
|
|
Jobs.enqueue_in(delay, :send_default_welcome_message, user_id: self.id)
|
|
end
|
|
end
|
|
|
|
self.add_to_class(:user, :enqueue_narrative_bot_job?) do
|
|
SiteSetting.discourse_narrative_bot_enabled &&
|
|
self.human? &&
|
|
!self.anonymous? &&
|
|
!self.staged &&
|
|
!SiteSetting.discourse_narrative_bot_ignored_usernames.split('|'.freeze).include?(self.username)
|
|
end
|
|
|
|
self.on(:post_created) do |post, options|
|
|
user = post.user
|
|
|
|
if user&.enqueue_narrative_bot_job? && !options[:skip_bot]
|
|
Jobs.enqueue(:bot_input,
|
|
user_id: user.id,
|
|
post_id: post.id,
|
|
input: :reply
|
|
)
|
|
end
|
|
end
|
|
|
|
self.on(:post_edited) do |post|
|
|
if post.user&.enqueue_narrative_bot_job?
|
|
Jobs.enqueue(:bot_input,
|
|
user_id: post.user.id,
|
|
post_id: post.id,
|
|
input: :edit
|
|
)
|
|
end
|
|
end
|
|
|
|
self.on(:post_destroyed) do |post, options, user|
|
|
if user&.enqueue_narrative_bot_job? && !options[:skip_bot]
|
|
Jobs.enqueue(:bot_input,
|
|
user_id: user.id,
|
|
post_id: post.id,
|
|
topic_id: post.topic_id,
|
|
input: :delete
|
|
)
|
|
end
|
|
end
|
|
|
|
self.on(:post_recovered) do |post, _, user|
|
|
if user&.enqueue_narrative_bot_job?
|
|
Jobs.enqueue(:bot_input,
|
|
user_id: user.id,
|
|
post_id: post.id,
|
|
input: :recover
|
|
)
|
|
end
|
|
end
|
|
|
|
self.add_model_callback(PostAction, :after_commit, on: :create) do
|
|
if self.post && self.user.enqueue_narrative_bot_job?
|
|
input =
|
|
case self.post_action_type_id
|
|
when *PostActionType.flag_types_without_custom.values
|
|
:flag
|
|
when PostActionType.types[:like]
|
|
:like
|
|
when PostActionType.types[:bookmark]
|
|
:bookmark
|
|
end
|
|
|
|
if input
|
|
Jobs.enqueue(:bot_input,
|
|
user_id: self.user.id,
|
|
post_id: self.post.id,
|
|
input: input
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
self.add_model_callback(Bookmark, :after_commit, on: :create) do
|
|
if self.post && self.user.enqueue_narrative_bot_job?
|
|
Jobs.enqueue(:bot_input, user_id: self.user_id, post_id: self.post_id, input: :bookmark)
|
|
end
|
|
end
|
|
|
|
self.on(:topic_notification_level_changed) do |_, user_id, topic_id|
|
|
user = User.find_by(id: user_id)
|
|
|
|
if user && user.enqueue_narrative_bot_job?
|
|
Jobs.enqueue(:bot_input,
|
|
user_id: user_id,
|
|
topic_id: topic_id,
|
|
input: :topic_notification_level_changed
|
|
)
|
|
end
|
|
end
|
|
|
|
self.on(:user_promoted) do |args|
|
|
promoted_from_tl1 = args[:new_trust_level] == TrustLevel[2] &&
|
|
args[:old_trust_level] == TrustLevel[1]
|
|
|
|
if SiteSetting.discourse_narrative_bot_enabled && promoted_from_tl1
|
|
# The event 'user_promoted' is sometimes called from inside a transaction.
|
|
# Use this helper to ensure the job is enqueued after commit to prevent
|
|
# any race conditions.
|
|
DB.after_commit do
|
|
Jobs.enqueue(:send_advanced_tutorial_message, user_id: args[:user_id])
|
|
end
|
|
end
|
|
end
|
|
|
|
UserAvatar.register_custom_user_gravatar_email_hash(
|
|
DiscourseNarrativeBot::BOT_USER_ID,
|
|
"discobot@discourse.org"
|
|
)
|
|
end
|