2022-08-22 11:46:49 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-09-01 11:31:27 +00:00
|
|
|
"crypto/sha512"
|
2022-08-22 11:46:49 +00:00
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
2022-11-26 18:44:11 +00:00
|
|
|
"io"
|
2022-11-25 00:10:27 +00:00
|
|
|
"net/http"
|
|
|
|
"encoding/json"
|
2022-11-26 18:44:11 +00:00
|
|
|
"compress/gzip"
|
2022-08-22 11:46:49 +00:00
|
|
|
|
|
|
|
"github.com/mattn/go-mastodon"
|
|
|
|
)
|
|
|
|
|
2022-09-08 12:26:25 +00:00
|
|
|
var (
|
|
|
|
c = mastodon.NewClient(&mastodon.Config{
|
2022-08-22 11:46:49 +00:00
|
|
|
Server: Conf.Server,
|
|
|
|
ClientID: Conf.ClientID,
|
|
|
|
ClientSecret: Conf.ClientSecret,
|
|
|
|
AccessToken: Conf.AccessToken,
|
|
|
|
})
|
|
|
|
|
2022-09-08 12:26:25 +00:00
|
|
|
ctx = context.Background()
|
|
|
|
|
|
|
|
my_account, _ = c.GetAccountCurrentUser(ctx)
|
2023-05-28 13:26:59 +00:00
|
|
|
boostsenabled = true
|
2022-09-08 12:26:25 +00:00
|
|
|
)
|
|
|
|
|
2022-11-25 00:10:27 +00:00
|
|
|
type APobject struct {
|
|
|
|
InReplyTo *string `json:"inReplyTo"`
|
|
|
|
}
|
|
|
|
|
2022-11-26 18:44:11 +00:00
|
|
|
//CheckAPReply return is reply bool of status
|
|
|
|
//Note: Not working with servers when they required Authorized fetch
|
|
|
|
//Note: By default false
|
2022-11-25 11:34:06 +00:00
|
|
|
func CheckAPReply(tooturl string) (bool) {
|
|
|
|
var apobj APobject
|
|
|
|
client := &http.Client{}
|
|
|
|
req, err := http.NewRequest(http.MethodGet, tooturl, nil)
|
|
|
|
if err != nil {
|
|
|
|
ErrorLogger.Println("Failed http request status AP")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
req.Header.Set("Accept", "application/activity+json")
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
if err != nil {
|
2022-11-26 18:44:11 +00:00
|
|
|
ErrorLogger.Printf("Server was not return AP object: %s", err)
|
2022-11-25 11:34:06 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
2022-11-26 18:44:11 +00:00
|
|
|
ErrorLogger.Printf("AP get: Server was return %s http code %s", resp.StatusCode, resp.Body)
|
2022-11-25 11:34:06 +00:00
|
|
|
return false
|
|
|
|
}
|
2022-11-26 18:44:11 +00:00
|
|
|
|
|
|
|
var reader io.ReadCloser
|
|
|
|
switch resp.Header.Get("Content-Encoding") {
|
|
|
|
case "gzip":
|
|
|
|
reader, err = gzip.NewReader(resp.Body)
|
|
|
|
defer reader.Close()
|
|
|
|
default:
|
|
|
|
reader = resp.Body
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewDecoder(reader).Decode(&apobj)
|
2022-11-25 11:34:06 +00:00
|
|
|
if err != nil {
|
|
|
|
ErrorLogger.Println("Failed decoding AP object")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if apobj.InReplyTo != nil {
|
2023-05-28 13:26:59 +00:00
|
|
|
WarnLogger.Printf("AP object of status detected reply: %s", &apobj.InReplyTo)
|
2022-11-25 11:34:06 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-09-08 12:26:25 +00:00
|
|
|
func RunBot() {
|
2022-08-22 11:46:49 +00:00
|
|
|
events, err := c.StreamingUser(ctx)
|
|
|
|
if err != nil {
|
2022-08-28 16:37:43 +00:00
|
|
|
ErrorLogger.Println("Streaming")
|
2022-08-22 11:46:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run bot
|
|
|
|
for {
|
|
|
|
notifEvent, ok := (<-events).(*mastodon.NotificationEvent)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
notif := notifEvent.Notification
|
|
|
|
|
|
|
|
// New follower
|
2022-09-08 15:00:27 +00:00
|
|
|
if notif.Type == "follow" {
|
2022-09-08 18:28:32 +00:00
|
|
|
acct := notif.Account.Acct
|
2022-09-08 15:00:27 +00:00
|
|
|
|
2022-09-08 12:26:25 +00:00
|
|
|
if !exist_in_database(acct) { // Add to db and post welcome message
|
2022-08-28 16:37:43 +00:00
|
|
|
InfoLogger.Printf("%s followed", acct)
|
|
|
|
|
2022-09-01 11:31:27 +00:00
|
|
|
add_to_db(acct)
|
2022-08-28 16:37:43 +00:00
|
|
|
InfoLogger.Printf("%s added to database", acct)
|
|
|
|
|
2022-09-06 09:31:05 +00:00
|
|
|
message := fmt.Sprintf("%s @%s", Conf.WelcomeMessage, acct)
|
2022-09-08 12:26:25 +00:00
|
|
|
_, err := postToot(message, "public")
|
2022-08-28 16:37:43 +00:00
|
|
|
if err != nil {
|
|
|
|
ErrorLogger.Println("Post welcome message")
|
|
|
|
}
|
|
|
|
InfoLogger.Printf("%s was welcomed", acct)
|
2022-08-25 19:23:44 +00:00
|
|
|
}
|
2022-08-22 11:46:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read message
|
2022-09-08 15:00:27 +00:00
|
|
|
if notif.Type == "mention" {
|
2022-09-23 02:10:13 +00:00
|
|
|
var account_id = []string{string(notif.Status.Account.ID)}
|
2022-09-08 15:00:27 +00:00
|
|
|
acct := notif.Status.Account.Acct
|
|
|
|
content := notif.Status.Content
|
|
|
|
tooturl := notif.Status.URL
|
|
|
|
|
2022-09-23 02:10:13 +00:00
|
|
|
// Fetch relationship
|
|
|
|
relationship, err := c.GetAccountRelationships(ctx, account_id)
|
|
|
|
if err != nil {
|
|
|
|
ErrorLogger.Println("Fetch relationship")
|
2022-09-09 05:44:09 +00:00
|
|
|
}
|
|
|
|
|
2022-09-08 18:28:32 +00:00
|
|
|
// Follow check
|
2022-09-23 02:10:13 +00:00
|
|
|
if relationship[0].FollowedBy {
|
2022-09-08 18:28:32 +00:00
|
|
|
if notif.Status.Visibility == "public" { // Reblog toot
|
2023-05-28 13:26:59 +00:00
|
|
|
if boostsenabled == false {
|
|
|
|
continue
|
|
|
|
}
|
2022-11-25 11:34:06 +00:00
|
|
|
var APreply bool
|
|
|
|
APreply = false
|
2022-11-25 00:10:27 +00:00
|
|
|
if notif.Status.InReplyToID == nil {
|
|
|
|
// Replies protection by get ActivityPub object
|
|
|
|
// (if breaking threads)
|
2022-11-25 11:34:06 +00:00
|
|
|
APreply = CheckAPReply(tooturl)
|
2022-11-25 00:10:27 +00:00
|
|
|
}
|
2022-11-25 11:34:06 +00:00
|
|
|
if notif.Status.InReplyToID == nil && APreply == false { // Not boost replies
|
2022-09-08 18:28:32 +00:00
|
|
|
// Duplicate protection
|
|
|
|
content_hash := sha512.New()
|
|
|
|
content_hash.Write([]byte(content))
|
|
|
|
hash := fmt.Sprintf("%x", content_hash.Sum(nil))
|
|
|
|
|
|
|
|
if !check_msg_hash(hash) {
|
|
|
|
save_msg_hash(hash)
|
|
|
|
InfoLogger.Printf("Hash of %s added to database", tooturl)
|
|
|
|
} else {
|
|
|
|
WarnLogger.Printf("%s is a duplicate and not boosted", tooturl)
|
|
|
|
}
|
2022-09-01 11:31:27 +00:00
|
|
|
|
2022-09-08 18:28:32 +00:00
|
|
|
// Add to db if needed
|
|
|
|
if !exist_in_database(acct) {
|
|
|
|
add_to_db(acct)
|
|
|
|
InfoLogger.Printf("%s added to database", acct)
|
|
|
|
}
|
2022-09-01 11:31:27 +00:00
|
|
|
|
2022-09-08 18:28:32 +00:00
|
|
|
// Message order
|
|
|
|
if check_order(acct) < Conf.Order_limit {
|
|
|
|
if check_ticket(acct) > 0 { // Message limit
|
|
|
|
take_ticket(acct)
|
|
|
|
InfoLogger.Printf("Ticket of %s was taken", acct)
|
2022-09-06 09:31:05 +00:00
|
|
|
|
2022-09-08 18:28:32 +00:00
|
|
|
count_order(acct)
|
|
|
|
InfoLogger.Printf("Order of %s was counted", acct)
|
2022-09-06 09:31:05 +00:00
|
|
|
|
2022-09-08 18:28:32 +00:00
|
|
|
c.Reblog(ctx, notif.Status.ID)
|
|
|
|
InfoLogger.Printf("Toot %s of %s was rebloged", tooturl, acct)
|
2022-08-28 16:37:43 +00:00
|
|
|
} else {
|
2022-09-08 18:28:32 +00:00
|
|
|
WarnLogger.Printf("%s haven't tickets", acct)
|
2022-08-25 19:23:44 +00:00
|
|
|
}
|
2022-08-28 16:37:43 +00:00
|
|
|
} else {
|
2022-09-08 18:28:32 +00:00
|
|
|
WarnLogger.Printf("%s order limit", acct)
|
2022-08-22 18:05:40 +00:00
|
|
|
}
|
2022-09-08 18:28:32 +00:00
|
|
|
} else {
|
|
|
|
WarnLogger.Printf("%s is reply and not boosted", tooturl)
|
|
|
|
}
|
|
|
|
} else if notif.Status.Visibility == "direct" { // Admin commands
|
|
|
|
for y := range Conf.Admins {
|
|
|
|
if acct == Conf.Admins[y] {
|
|
|
|
recmd := regexp.MustCompile(`<[^>]+>`)
|
|
|
|
command := recmd.ReplaceAllString(content, "")
|
|
|
|
args := strings.Split(command, " ")
|
|
|
|
|
|
|
|
if len(args) == 3 {
|
|
|
|
mID := mastodon.ID((args[2]))
|
|
|
|
|
|
|
|
switch args[1] {
|
2022-11-25 13:03:41 +00:00
|
|
|
case "boost":
|
|
|
|
c.Reblog(ctx, mID)
|
|
|
|
WarnLogger.Printf("%s was rebloged", mID)
|
2022-09-08 18:28:32 +00:00
|
|
|
case "unboost":
|
|
|
|
c.Unreblog(ctx, mID)
|
|
|
|
WarnLogger.Printf("%s was unrebloged", mID)
|
|
|
|
case "delete":
|
|
|
|
c.DeleteStatus(ctx, mID)
|
|
|
|
WarnLogger.Printf("%s was deleted", mID)
|
2023-05-28 13:26:59 +00:00
|
|
|
case "disable":
|
|
|
|
boostsenabled = false
|
|
|
|
WarnLogger.Printf("Reblogs disabled by admin")
|
|
|
|
case "enable":
|
|
|
|
boostsenabled = true
|
|
|
|
WarnLogger.Printf("Reblogs enabled by admin")
|
2022-09-08 18:28:32 +00:00
|
|
|
default:
|
|
|
|
WarnLogger.Printf("%s entered wrong command", acct)
|
2022-08-22 11:46:49 +00:00
|
|
|
}
|
2023-05-28 13:26:59 +00:00
|
|
|
} else if len(args) == 2 {
|
|
|
|
switch args[1] {
|
|
|
|
case "disable":
|
|
|
|
boostsenabled = false
|
|
|
|
WarnLogger.Printf("Reblogs disabled by admin")
|
|
|
|
case "enable":
|
|
|
|
boostsenabled = true
|
|
|
|
WarnLogger.Printf("Reblogs enabled by admin")
|
|
|
|
default:
|
|
|
|
WarnLogger.Printf("%s entered wrong command", acct)
|
|
|
|
}
|
2022-08-28 16:37:43 +00:00
|
|
|
} else {
|
2022-09-08 18:28:32 +00:00
|
|
|
WarnLogger.Printf("%s entered wrong command", acct)
|
2022-08-22 11:46:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-08 18:28:32 +00:00
|
|
|
} else {
|
|
|
|
WarnLogger.Printf("%s is not public toot and not boosted", tooturl)
|
2022-08-22 11:46:49 +00:00
|
|
|
}
|
2022-09-08 18:28:32 +00:00
|
|
|
} else { // Notify user
|
|
|
|
if got_notice(acct) == 0 {
|
|
|
|
if !exist_in_database(acct) {
|
|
|
|
add_to_db(acct)
|
|
|
|
InfoLogger.Printf("%s added to database", acct)
|
|
|
|
}
|
2022-11-24 20:10:33 +00:00
|
|
|
if notif.Status.InReplyToID == nil { // Prevent spam in DM if status is reply
|
|
|
|
message := fmt.Sprintf("@%s %s", acct, Conf.NotFollowedMessage)
|
|
|
|
_, err := postToot(message, "direct")
|
|
|
|
if err != nil {
|
|
|
|
ErrorLogger.Printf("Notify %s", acct)
|
|
|
|
}
|
|
|
|
InfoLogger.Printf("%s has been notified", acct)
|
|
|
|
mark_notice(acct)
|
|
|
|
if got_notice(acct) == 0 {
|
|
|
|
InfoLogger.Printf("Dooble notice marked")
|
|
|
|
mark_notice(acct)
|
|
|
|
}
|
|
|
|
InfoLogger.Printf("%s marked notification in database", acct)
|
|
|
|
} else {
|
|
|
|
InfoLogger.Printf("%s their status is reply, not notified", acct)
|
2022-09-08 12:26:25 +00:00
|
|
|
}
|
2022-09-08 18:28:32 +00:00
|
|
|
|
2022-09-08 12:26:25 +00:00
|
|
|
}
|
2022-08-22 11:46:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|