💎

【令和6年保存版!】Rubyでライブラリ(gems)非依存でTwitter(X)・Bluesky・Misskeyに投稿するには

2024/04/30に公開

TwiterやBlueskyでは、ライブラリが提供されていますが、AWS Lambdaなどで容量を軽くしたいとか、Twitterのgemはメンテナンスされなくなってしまった、Misskeyに至ってはgemが提供されていないなどの理由で、外部ライブラリ非依存でコードを使いたい、そんなときにGPTさんたちに聞いたら教えてくれたのでメモとして残します。それぞれちゃんと動いています。

いずれも環境変数に認証情報をおいているほか、post_textに投稿内容を入れています。

Twitter(X)

140文字にカットしています。重複投稿は検出していません。

require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'openssl'

  ### 認証情報生成
  # ここでは環境変数から取り出している
  # Twitter認証情報
  tw_consumer_key = ENV["tw_consumer_key"]
  tw_consumer_secret = ENV["tw_consumer_secret"]
  tw_access_token = ENV["tw_access_token"]
  tw_access_token_secret  = ENV["tw_access_token_secret"]
  tw_create_tweet_url = "https://api.twitter.com/2/tweets"
  
  
  ### 投稿前準備完了
  puts "投稿準備"
  puts "投稿用テキスト: #{post_text}"
  
  ### Twitterへ投稿
  # Twitterへの投稿
  timestamp = Time.now.to_i
  nonce = SecureRandom.hex
  signature_params = {
  oauth_consumer_key: tw_consumer_key,
  oauth_nonce: nonce,
  oauth_signature_method: 'HMAC-SHA1',
  oauth_timestamp: timestamp,
  oauth_token: tw_access_token,
  oauth_version: '1.0'
  }
  signature_base_string = "POST&#{URI.encode_www_form_component(tw_create_tweet_url)}&#{URI.encode_www_form_component(signature_params.map { |k, v| "#{k}=#{v}" }.join('&'))}"
  signing_key = "#{URI.encode_www_form_component(tw_consumer_secret)}&#{URI.encode_www_form_component(tw_access_token_secret)}"
  signature = Base64.strict_encode64(OpenSSL::HMAC.digest('sha1', signing_key, signature_base_string))

  headers = {
    'Authorization' => "OAuth oauth_consumer_key=\"#{tw_consumer_key}\", oauth_nonce=\"#{nonce}\", oauth_signature=\"#{URI.encode_www_form_component(signature)}\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"#{timestamp}\", oauth_token=\"#{tw_access_token}\", oauth_version=\"1.0\"",
  'Content-Type' => 'application/json'
  }
  body = {text: post_text.gsub(/〈[^〉]*〉/, "").slice(0, 140)}.to_json

  uri = URI.parse(tw_create_tweet_url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  request = Net::HTTP::Post.new(uri.request_uri, headers)
  request.body = body
  puts "request.body: #{request.body}"
  
  begin
    twitter_response = http.request(request)
    puts "twitterに投稿しました"
    puts twitter_response.to_s
    puts twitter_response.body.force_encoding("UTF-8")
    # 投稿終わり
    puts "投稿スクリプト終わり。Well done!"
  rescue => e
    puts "twitter投稿エラー"
    puts twitter_response.to_s
    puts twitter_response.body.force_encoding("UTF-8")
    puts e.class
    puts e.message
    puts e.backtrace
  end

Bluesky

require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'openssl'

  ### 認証情報生成
  # ここでは環境変数から取り出している
  # Bluesky認証情報
  bs_username = ENV["bs_username"]
  bs_password = ENV["bs_password"]
  bs_pds_url = "https://bsky.social"
  
  ### 投稿前準備完了
  puts "投稿準備"
  puts "投稿用テキスト: #{post_text}"  
  
  ###
  # Blueskyへの投稿
  uri = URI.parse("#{bs_pds_url}/xrpc/com.atproto.server.createSession")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
  request.body = { identifier: bs_username, password: bs_password }.to_json
  response = http.request(request)
  session = JSON.parse(response.body)

  uri = URI.parse("#{bs_pds_url}/xrpc/com.atproto.repo.createRecord")
  request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{session['accessJwt']}")
  request.body = {
    collection: 'app.bsky.feed.post',
    repo: session['did'],
    record: {
      text: post_text,
      createdAt: Time.now.utc.iso8601
    }
  }.to_json

  begin
    bluesky_response = http.request(request)
    puts "blueskyに投稿しました"
    puts bluesky_response.to_s
    puts bluesky_response.body.to_s
  rescue => e
    puts "bluesky投稿エラー"
    puts bluesky_response.to_s
    puts bluesky_response.body.to_s
    puts e.class
    puts e.message
    puts e.backtrace
    return false
  end

Misskey.io

require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'openssl'

  ### 認証情報生成
  # ここでは環境変数から取り出している
  # Misskey認証情報
  mk_access_token = ENV["mk_access_token"]
  
  ### 投稿前準備完了
  puts "投稿準備"
  puts "投稿用テキスト: #{post_text}"
  
  
  ###
  # misskeyへの投稿
  uri = URI.parse("https://misskey.io/api/notes/create")
  request = Net::HTTP::Post.new(uri)
  request.content_type = "application/json"
  request["Authorization"] = "Bearer #{mk_access_token}"
  request.body = {"text": post_text}.to_json

  req_options = {use_ssl: uri.scheme == "https"}
  
  begin
    response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
      http.request(request)
    end
    
    if response.code == '200' then
      # 投稿が成功しました
      puts "Misskeyへの投稿が成功しました。"
    else
      puts "Misskeyへの投稿が失敗しました。"
      raise
    end
    
    puts response.to_s
    puts response.body.force_encoding("UTF-8")
    # 投稿終わり
    puts "投稿スクリプト終わり。Well done!"

  rescue => e
    puts "bluesky投稿エラー"
    puts e.class
    puts e.message
    puts e.backtrace
    puts response.to_s
    puts response.body.force_encoding("UTF-8")
    return false
  end

Discussion