handler.ex 3.83 KB
Newer Older
lain's avatar
lain committed
1
2
defmodule Pleroma.BBS.Handler do
  use Sshd.ShellHandler
lain's avatar
lain committed
3
  alias Pleroma.Activity
lain's avatar
lain committed
4
  alias Pleroma.Web.ActivityPub.ActivityPub
lain's avatar
lain committed
5
  alias Pleroma.Web.CommonAPI
lain's avatar
lain committed
6
7

  def on_shell(username, _pubkey, _ip, _port) do
lain's avatar
lain committed
8
    :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!")
lain's avatar
lain committed
9
    user = Pleroma.User.get_cached_by_nickname(to_string(username))
lain's avatar
lain committed
10
11
    Logger.debug("#{inspect(user)}")
    loop(run_state(user: user))
lain's avatar
lain committed
12
13
14
  end

  def on_connect(username, ip, port, method) do
lain's avatar
lain committed
15
    Logger.debug(fn ->
lain's avatar
lain committed
16
      """
lain's avatar
lain committed
17
18
19
      Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{
        inspect(port)
      } using #{inspect(method)}
lain's avatar
lain committed
20
      """
lain's avatar
lain committed
21
    end)
lain's avatar
lain committed
22
23
24
  end

  def on_disconnect(username, ip, port) do
lain's avatar
lain committed
25
26
27
    Logger.debug(fn ->
      "Disconnecting SSH shell for #{username} from #{inspect(ip)}:#{inspect(port)}"
    end)
lain's avatar
lain committed
28
29
30
31
  end

  defp loop(state) do
    self_pid = self()
lain's avatar
lain committed
32
33
34
    counter = state.counter
    prefix = state.prefix
    user = state.user
lain's avatar
lain committed
35
36

    input = spawn(fn -> io_get(self_pid, prefix, counter, user.nickname) end)
lain's avatar
lain committed
37
    wait_input(state, input)
lain's avatar
lain committed
38
39
40
41
  end

  def puts_activity(activity) do
    status = Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{activity: activity})
lain's avatar
lain committed
42
    IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})")
lain's avatar
lain committed
43
44
45
46
47
48
49
50
51
    IO.puts(HtmlSanitizeEx.strip_tags(status.content))
    IO.puts("")
  end

  def handle_command(state, "help") do
    IO.puts("Available commands:")
    IO.puts("help - This help")
    IO.puts("home - Show the home timeline")
    IO.puts("p <text> - Post the given text")
lain's avatar
lain committed
52
    IO.puts("r <id> <text> - Reply to the post with the given id")
lain's avatar
lain committed
53
54
55
56
57
    IO.puts("quit - Quit")

    state
  end

lain's avatar
lain committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  def handle_command(%{user: user} = state, "r " <> text) do
    text = String.trim(text)
    [activity_id, rest] = String.split(text, " ", parts: 2)

    with %Activity{} <- Activity.get_by_id(activity_id),
         {:ok, _activity} <-
           CommonAPI.post(user, %{"status" => rest, "in_reply_to_status_id" => activity_id}) do
      IO.puts("Replied!")
    else
      _e -> IO.puts("Could not reply...")
    end

    state
  end

lain's avatar
lain committed
73
74
75
76
77
78
79
80
  def handle_command(%{user: user} = state, "p " <> text) do
    text = String.trim(text)

    with {:ok, _activity} <- CommonAPI.post(user, %{"status" => text}) do
      IO.puts("Posted!")
    else
      _e -> IO.puts("Could not post...")
    end
lain's avatar
lain committed
81

lain's avatar
lain committed
82
83
84
85
86
    state
  end

  def handle_command(state, "home") do
    user = state.user
lain's avatar
lain committed
87

lain's avatar
lain committed
88
89
    params =
      %{}
lain's avatar
lain committed
90
      |> Map.put("type", ["Create"])
lain's avatar
lain committed
91
92
93
94
95
96
97
98
99
      |> Map.put("blocking_user", user)
      |> Map.put("muting_user", user)
      |> Map.put("user", user)

    activities =
      [user.ap_id | user.following]
      |> ActivityPub.fetch_activities(params)
      |> ActivityPub.contain_timeline(user)

lain's avatar
lain committed
100
    Enum.each(activities, fn activity ->
lain's avatar
lain committed
101
102
103
104
105
106
      puts_activity(activity)
    end)

    state
  end

lain's avatar
lain committed
107
  def handle_command(state, command) do
lain's avatar
lain committed
108
    IO.puts("Unknown command '#{command}'")
lain's avatar
lain committed
109
    state
lain's avatar
lain committed
110
111
112
113
114
  end

  defp wait_input(state, input) do
    receive do
      {:input, ^input, "quit\n"} ->
lain's avatar
lain committed
115
        IO.puts("Exiting...")
lain's avatar
lain committed
116
117
118
119

      {:input, ^input, code} when is_binary(code) ->
        code = String.trim(code)

lain's avatar
lain committed
120
        state = handle_command(state, code)
lain's avatar
lain committed
121
122
123
124

        loop(%{state | counter: state.counter + 1})

      {:error, :interrupted} ->
lain's avatar
lain committed
125
        IO.puts("Caught Ctrl+C...")
lain's avatar
lain committed
126
127
128
        loop(%{state | counter: state.counter + 1})

      {:input, ^input, msg} ->
lain's avatar
lain committed
129
        :ok = Logger.warn("received unknown message: #{inspect(msg)}")
lain's avatar
lain committed
130
131
132
133
134
135
136
137
138
139
        loop(%{state | counter: state.counter + 1})
    end
  end

  defp run_state(opts) do
    %{prefix: "pleroma", counter: 1, user: opts[:user]}
  end

  defp io_get(pid, prefix, counter, username) do
    prompt = prompt(prefix, counter, username)
lain's avatar
lain committed
140
    send(pid, {:input, self(), IO.gets(:stdio, prompt)})
lain's avatar
lain committed
141
142
143
144
145
146
147
  end

  defp prompt(prefix, counter, username) do
    prompt = "#{username}@#{prefix}:#{counter}>"
    prompt <> " "
  end
end