Skip to content

Latest commit

 

History

History
211 lines (174 loc) · 7.46 KB

twitter-oauth.asc

File metadata and controls

211 lines (174 loc) · 7.46 KB

与 Twitter 接口通信

首先访问 https://apps.twitter.com 新建一个 app,这样我们就有了两个数据:

  1. Consumer Key

  2. Consumer Secret

记得配置 Callback URLs - 比如开发环境下我将新建一个 /auth_callback 路由用于处理回调,就需要将 http://127.0.0.1:4000/auth_callback 加入 Callback URLs

接下来与 twitter 的一切通信都交给 https://github.com/parroty/extwitter 库。

mix.exs 文件的 deps 中新增 {:extwitter, "~> 0.9.3"},并执行 mix deps.get 安装 extwitter

安装完 extwitter 后,按说明在 dev.exs 添加如下配置(记得要重启 Phoenix):

config/dev.exs
+
+# Configures extwitter oauth
+config :extwitter, :oauth, [
+  consumer_key: System.get_env("TWITTER_CONSUMER_KEY"),
+  consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET")
+]

我们从环境变量中读取 consumer_keyconsumer_secret 的值,access_tokenaccess_token_secret 值暂时不设定,后面在代码中动态设置。

不过等一等,access_token_secret?我在 User 结构中可是只定义了 access_token

显然,我们需要在 User 结构中新增 access_token_secret 字段。

mix ecto.gen.migration

我们要借助 mix ecto.gen.migration

在命令行下执行:

$ mix ecto.gen.migration add_access_token_secret_to_users
Generated tweet_bot app
* creating priv/repo/migrations/20181203122738_add_access_token_secret_to_users.exs

打开新建的文件,目前内容如下:

priv/repo/migrations/20181203122738_add_access_token_secret_to_users.exs
defmodule TweetBot.Repo.Migrations.AddAccessTokenSecretToUsers do
  use Ecto.Migration

  def change do

  end
end

调整如下:

   def change do
-
+    alter table(:users) do
+      add(:access_token_secret, :string)
+    end
   end

此外,我们还需要调整 user.ex

 defmodule TweetBot.Accounts.User do
   use Ecto.Schema
   import Ecto.Changeset

   schema "users" do
     field(:access_token, :string)
+    field(:access_token_secret, :string)
     field(:from_id, :string)

     timestamps()
   end

   @doc false
   def changeset(user, attrs) do
     user
-    |> cast(attrs, [:from_id, :access_token])
+    |> cast(attrs, [:from_id, :access_token, :access_token_secret])
   end
 end

接着运行 mix ecto.migrate 来让上述修改生效。

回调

再回到 twitter OAuth 流程,我们需要提交一个回调地址,这样用户登录后,twitter 会跳到该回调 - 并且携带 oauth_tokenoauth_verifier,接着我们再提交这两个参数去换取 access_tokenaccess_token_secret

我们需要在 router.ex 中新增一个路由:

lib/tweet_bot_web/router.ex
   scope "/", TweetBotWeb do
     pipe_through :browser # Use the default browser stack

     get("/", PageController, :index)
+    get("/auth_callback", AuthController, :callback)
   end

接着创建 lib/tweet_bot_web/controllers/auth_controller.ex 文件:

lib/tweet_bot_web/controllers/auth_controller.ex
+defmodule TweetBotWeb.AuthController do
+  use TweetBotWeb, :controller
+
+  def callback(conn, _params) do
+  end
+end

启动 OAuth

我们启动 OAuth 流程的时机是在接收到用户发来 /start。所以让我们回到 twitter_controller.ex 中,调整代码如下:

   def index(conn, %{"message" => %{"from" => %{"id" => from_id}, "text" => text}}) do
-    sendMessage(from_id, "你好")
+    case text do
+      "/start" ->
+        token =
+          ExTwitter.request_token(
+            URI.encode_www_form(Routes.auth_url(conn, :callback))
+          )
+
+        {:ok, authenticate_url} = ExTwitter.authenticate_url(token.oauth_token)
+
+        sendMessage(
+          from_id,
+          "请点击链接登录您的 Twitter 账号进行授权:<a href='" <> authenticate_url <> "'>登录 Twitter</a>",
+          parse_mode: "HTML"
+        )
+
+      _ ->
+        sendMessage(from_id, "你好")
+    end
+
     json(conn, %{})
   end

尝试给测试机器发送 /start,好一会儿,开发服务器下报告错误:

[error] #PID<0.474.0> running TweetBotWeb.Endpoint terminated
Request: POST /api/twitter
** (exit) an exception was raised:
    ** (MatchError) no match of right hand side value: {:error, {:failed_connect, [{:to_address, {'api.twitter.com', 443}}, {:inet, [:inet], :etimedout}]}}
        (extwitter) lib/extwitter/api/auth.ex:10: ExTwitter.API.Auth.request_token/1
        (tweet_bot) lib/tweet_bot_web/controllers/twitter_controller.ex:9: TweetBotWeb.TwitterController.index/2
        (tweet_bot) lib/tweet_bot_web/controllers/twitter_controller.ex:1: TweetBotWeb.TwitterController.action/2
        (tweet_bot) lib/tweet_bot_web/controllers/twitter_controller.ex:1: TweetBotWeb.TwitterController.phoenix_controller_pipeline/2
        (tweet_bot) lib/tweet_bot_web/endpoint.ex:1: TweetBotWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (tweet_bot) lib/tweet_bot_web/endpoint.ex:1: TweetBotWeb.Endpoint.plug_builder_call/2
        (tweet_bot) lib/plug/debugger.ex:102: TweetBotWeb.Endpoint."call (overridable 3)"/2
        (tweet_bot) lib/tweet_bot_web/endpoint.ex:1: TweetBotWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) /Users/sam/Documents/githubRepos/tweet_bot/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

是了,extwitter 要与 twitter 通信,同样需要配置代理。打开 dev.exs 文件,新增如下内容:

config/dev.exs
+]
+
+# Configures extwitter proxy
+config :extwitter, :proxy, [
+  server: "127.0.0.1",
+  port: 1087
 ]

重启开发服务器。再发送 /start 给机器人 - 收到登录链接了。

不过且慢点击登录链接。在点击前,我们还需要填充 auth_controller.ex 中的 callback

-  def callback(conn, _params) do
+  def callback(conn, %{"oauth_token" => oauth_token, "oauth_verifier" => oauth_verifier}) do
+    # 获取 access token
+    {:ok, token} = ExTwitter.access_token(oauth_verifier, oauth_token)
+    IO.inspect(token)
+    text(conn, "授权成功,请关闭此页面")
   end

跑一遍流程就会发现,我们已经成功获取到 access_tokenaccess_token_secret 了 - 只不过,响应中的名称与我们预想中的不一样,一个是 oauth_token,一个是 oauth_token_secret。拿到这俩个数据后,我们就可以以用户的身份发推了:

     {:ok, token} = ExTwitter.access_token(oauth_verifier, oauth_token)
-    IO.inspect(token)
+    ExTwitter.configure(
+      :process,
+      Enum.concat(
+        ExTwitter.Config.get_tuples,
+        [ access_token: token.oauth_token,
+          access_token_secret: token.oauth_token_secret ]
+      )
+    )
+    ExTwitter.update("I just sign up telegram bot tweet_for_me_bot.")
     text(conn, "授权成功,请关闭此页面")

发送 /start 给机器人,点击返回的链接,授权,查看 twitter 主页,有了:I just sign up telegram bot tweet_for_me_bot.