Enable sending emoji reactions in DMs

This commit is contained in:
Alessio 2024-07-28 14:09:41 -07:00
parent 7244bf51e9
commit b4384f9cc4
5 changed files with 93 additions and 1 deletions

View File

@ -215,6 +215,15 @@ func main() {
} }
send_dm(target, args[2], val) send_dm(target, args[2], val)
} }
case "send_dm_reacc":
if len(args) != 4 {
die("", true, 1)
}
val, err := strconv.Atoi(args[2])
if err != nil {
panic(err)
}
send_dm_reacc(args[1], val, args[3]) // room, message, emoji
default: default:
die(fmt.Sprintf("Invalid operation: %s", operation), true, 3) die(fmt.Sprintf("Invalid operation: %s", operation), true, 3)
} }
@ -546,3 +555,20 @@ func send_dm(room_id string, text string, in_reply_to_id int) {
profile.SaveTweetTrove(trove, true) profile.SaveTweetTrove(trove, true)
happy_exit(fmt.Sprintf("Saved %d messages from %d chats", len(trove.Messages), len(trove.Rooms)), nil) happy_exit(fmt.Sprintf("Saved %d messages from %d chats", len(trove.Messages), len(trove.Rooms)), nil)
} }
func send_dm_reacc(room_id string, in_reply_to_id int, reacc string) {
room, err := profile.GetChatRoom(scraper.DMChatRoomID(room_id))
if err != nil {
die(fmt.Sprintf("No such chat room: %d", in_reply_to_id), false, 1)
}
_, err = profile.GetChatMessage(scraper.DMMessageID(in_reply_to_id))
if err != nil {
die(fmt.Sprintf("No such message: %d", in_reply_to_id), false, 1)
}
err = scraper.SendDMReaction(room.ID, scraper.DMMessageID(in_reply_to_id), reacc)
if err != nil {
die(fmt.Sprintf("Failed to react to message:\n %s", err.Error()), false, 1)
}
happy_exit("Sent the reaction", nil)
}

View File

@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
@ -77,6 +78,30 @@ func (app *Application) message_detail(w http.ResponseWriter, r *http.Request) {
return return
} }
// Handle reactions
if len(parts) == 1 && parts[0] == "reacc" {
var data struct {
MessageID scraper.DMMessageID `json:"message_id,string"`
Reacc string `json:"reacc"`
}
data_, err := io.ReadAll(r.Body)
panic_if(err)
panic_if(json.Unmarshal(data_, &data))
panic_if(scraper.SendDMReaction(room_id, data.MessageID, data.Reacc))
dm_message := global_data.Messages[data.MessageID]
dm_message.Reactions[app.ActiveUser.ID] = scraper.DMReaction{
ID: 0, // Hopefully will be OK temporarily
DMMessageID: dm_message.ID,
SenderID: app.ActiveUser.ID,
SentAt: scraper.Timestamp{time.Now()},
Emoji: data.Reacc,
}
global_data.Messages[dm_message.ID] = dm_message
app.buffered_render_htmx(w, "message", global_data, dm_message)
return
}
// First send a message, if applicable // First send a message, if applicable
if is_sending { if is_sending {
app.message_send(w, r) app.message_send(w, r)

View File

@ -1,7 +1,7 @@
{{define "message"}} {{define "message"}}
{{$user := (user .SenderID)}} {{$user := (user .SenderID)}}
{{$is_us := (eq .SenderID (active_user).ID)}} {{$is_us := (eq .SenderID (active_user).ID)}}
<div class="dm-message {{if $is_us}} our-message {{end}}" data-message-id="{{ .ID }}" hx-ext="json-enc"> <div class="dm-message {{if $is_us}} our-message {{end}}" data-message-id="{{ .ID }}" hx-ext="json-enc" hx-swap="outerHTML">
<div class="dm-message__row row"> <div class="dm-message__row row">
<div class="dm-message__sender-profile-img"> <div class="dm-message__sender-profile-img">
{{template "circle-profile-img" $user}} {{template "circle-profile-img" $user}}
@ -65,6 +65,18 @@
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="dm-message__emoji-button-container">
<div class="dm-message__emoji-button button" onclick="
show_emoji_picker(function(emoji_info) {
htmx.ajax('POST', '/messages/{{$.DMChatRoomID}}/reacc', {values: {
message_id: '{{$.ID}}',
reacc: emoji_info.unicode,
}, source: '[data-message-id=\'{{$.ID}}\']'});
});
">
<img class="svg-icon" src="/static/icons/emoji-react.svg" width="24" height="24"/>
</div>
</div>
</div> </div>
<div class="dm-message__reactions"> <div class="dm-message__reactions">
{{range .Reactions}} {{range .Reactions}}

View File

@ -522,6 +522,29 @@ func (api *API) SendDMMessage(room_id DMChatRoomID, text string, in_reply_to_id
return result, err return result, err
} }
// Send a reacc
func (api *API) SendDMReaction(room_id DMChatRoomID, message_id DMMessageID, reacc string) error {
url := "https://twitter.com/i/api/graphql/VyDyV9pC2oZEj6g52hgnhA/useDMReactionMutationAddMutation"
body := `{"variables":{"conversationId":"` + string(room_id) + `","messageId":"` + fmt.Sprint(message_id) +
`","reactionTypes":["Emoji"],"emojiReactions":["` + reacc + `"]},"queryId":"VyDyV9pC2oZEj6g52hgnhA"}`
type SendDMResponse struct {
Data struct {
CreateDmReaction struct {
Typename string `json:"__typename"`
} `json:"create_dm_reaction"`
} `json:"data"`
}
var result SendDMResponse
err := api.do_http_POST(url, body, &result)
if err != nil {
return fmt.Errorf("Error executing HTTP POST:\n %w", err)
}
if result.Data.CreateDmReaction.Typename != "CreateDMReactionSuccess" {
return fmt.Errorf("Unexpected result sending DM reaction: %s", result.Data.CreateDmReaction.Typename)
}
return nil
}
// Mark a chat as read. // Mark a chat as read.
func (api *API) MarkDMChatRead(room_id DMChatRoomID, read_message_id DMMessageID) { func (api *API) MarkDMChatRead(room_id DMChatRoomID, read_message_id DMMessageID) {
url := fmt.Sprintf("https://twitter.com/i/api/1.1/dm/conversation/%s/mark_read.json", room_id) url := fmt.Sprintf("https://twitter.com/i/api/1.1/dm/conversation/%s/mark_read.json", room_id)

View File

@ -89,6 +89,12 @@ func SendDMMessage(room_id DMChatRoomID, text string, in_reply_to_id DMMessageID
} }
return dm_response.ToTweetTrove() return dm_response.ToTweetTrove()
} }
func SendDMReaction(room_id DMChatRoomID, message_id DMMessageID, reacc string) error {
if !the_api.IsAuthenticated {
log.Fatalf("Fetching DMs can only be done when authenticated. Please provide `--session [user]`")
}
return the_api.SendDMReaction(room_id, message_id, reacc)
}
func MarkDMChatRead(room_id DMChatRoomID, read_message_id DMMessageID) { func MarkDMChatRead(room_id DMChatRoomID, read_message_id DMMessageID) {
if !the_api.IsAuthenticated { if !the_api.IsAuthenticated {
log.Fatalf("Writing DMs can only be done when authenticated. Please provide `--session [user]`") log.Fatalf("Writing DMs can only be done when authenticated. Please provide `--session [user]`")