Class: ChatSDK::X::Adapter

Inherits:
Adapter::Base
  • Object
show all
Defined in:
lib/chat_sdk/x/adapter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(access_token: nil, consumer_secret: nil, user_id: nil) ⇒ Adapter

Returns a new instance of Adapter.

Raises:

  • (ChatSDK::ConfigurationError)


14
15
16
17
18
19
20
21
22
23
# File 'lib/chat_sdk/x/adapter.rb', line 14

def initialize(access_token: nil, consumer_secret: nil, user_id: nil)
  @access_token = access_token || ENV["X_ACCESS_TOKEN"]
  @consumer_secret = consumer_secret || ENV["X_CONSUMER_SECRET"]
  @user_id = user_id || ENV["X_USER_ID"]

  raise ChatSDK::ConfigurationError, "X access_token required" unless @access_token
  raise ChatSDK::ConfigurationError, "X consumer_secret required" unless @consumer_secret

  @client = ApiClient.new(@access_token)
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



12
13
14
# File 'lib/chat_sdk/x/adapter.rb', line 12

def client
  @client
end

Instance Method Details

#ack_response(rack_request) ⇒ Object



49
50
51
52
53
54
55
56
57
58
# File 'lib/chat_sdk/x/adapter.rb', line 49

def ack_response(rack_request)
  return nil unless rack_request.get?

  crc_token = rack_request.params["crc_token"]
  return nil unless crc_token

  response_token = "sha256=#{Base64.strict_encode64(OpenSSL::HMAC.digest("SHA256", @consumer_secret, crc_token))}"

  [200, {"content-type" => "application/json"}, [JSON.generate({"response_token" => response_token})]]
end

#add_reaction(channel_id:, message_id:, emoji:) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



103
104
105
# File 'lib/chat_sdk/x/adapter.rb', line 103

def add_reaction(channel_id:, message_id:, emoji:) # rubocop:disable Lint/UnusedMethodArgument
  @client.like_tweet(user_id: @user_id, tweet_id: message_id)
end

#mention(user_id) ⇒ Object



115
116
117
# File 'lib/chat_sdk/x/adapter.rb', line 115

def mention(user_id)
  "@#{user_id}"
end

#nameObject



25
26
27
# File 'lib/chat_sdk/x/adapter.rb', line 25

def name
  :x
end

#open_dm(user_id) ⇒ Object



111
112
113
# File 'lib/chat_sdk/x/adapter.rb', line 111

def open_dm(user_id)
  user_id
end

#parse_events(rack_request) ⇒ Object



60
61
62
63
64
65
# File 'lib/chat_sdk/x/adapter.rb', line 60

def parse_events(rack_request)
  payload = read_json_body(rack_request)
  EventParser.parse(payload, bot_user_id: @user_id)
rescue JSON::ParserError
  []
end

#post_message(channel_id:, message:, thread_id: nil) ⇒ Object

Outbound



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/chat_sdk/x/adapter.rb', line 68

def post_message(channel_id:, message:, thread_id: nil)
  msg = ChatSDK::PostableMessage.from(message)
  text = msg.text || msg.card&.fallback_text || ""

  if thread_id&.start_with?("x:dm:")
    result = @client.send_dm(participant_id: channel_id, text: text)
    message_id = result.dig("dm_event", "id") || result["id"]

    ChatSDK::Message.new(
      id: message_id,
      text: text,
      author: ChatSDK::Author.new(id: @user_id || "bot", name: "bot", platform: :x, bot: true),
      thread_id: thread_id,
      channel_id: channel_id,
      platform: :x,
      raw: result
    )
  else
    payload = {text: text}
    payload[:reply] = {in_reply_to_tweet_id: channel_id} if channel_id && thread_id
    result = @client.create_tweet(**payload)
    tweet_id = result.dig("data", "id") || result["id"]

    ChatSDK::Message.new(
      id: tweet_id,
      text: text,
      author: ChatSDK::Author.new(id: @user_id || "bot", name: "bot", platform: :x, bot: true),
      thread_id: thread_id || "x:post:#{tweet_id}",
      channel_id: channel_id,
      platform: :x,
      raw: result
    )
  end
end

#remove_reaction(channel_id:, message_id:, emoji:) ⇒ Object

rubocop:disable Lint/UnusedMethodArgument



107
108
109
# File 'lib/chat_sdk/x/adapter.rb', line 107

def remove_reaction(channel_id:, message_id:, emoji:) # rubocop:disable Lint/UnusedMethodArgument
  @client.unlike_tweet(user_id: @user_id, tweet_id: message_id)
end

#render(postable_message) ⇒ Object



119
120
121
# File 'lib/chat_sdk/x/adapter.rb', line 119

def render(postable_message)
  postable_message.text
end

#verify_request!(rack_request) ⇒ Object

Inbound



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/chat_sdk/x/adapter.rb', line 30

def verify_request!(rack_request)
  body = rack_request.body.read
  rack_request.body.rewind

  signature = rack_request.get_header("HTTP_X_TWITTER_WEBHOOKS_SIGNATURE")

  unless signature
    raise ChatSDK::SignatureVerificationError, "Missing X signature header"
  end

  expected = "sha256=#{Base64.strict_encode64(OpenSSL::HMAC.digest("SHA256", @consumer_secret, body))}"

  unless Rack::Utils.secure_compare(signature, expected)
    raise ChatSDK::SignatureVerificationError, "Invalid X signature"
  end

  true
end