2023-08-23 02:06:37 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ekzyis/sn-goapi"
|
|
|
|
)
|
|
|
|
|
2024-02-26 14:43:34 +00:00
|
|
|
type NostrClient struct {
|
|
|
|
Url string
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
2023-08-23 02:06:37 +00:00
|
|
|
var (
|
|
|
|
TwitterUrlRegexp = regexp.MustCompile(`^(?:https?:\/\/)?((www\.)?(twitter|x)\.com)\/`)
|
2023-08-31 08:20:18 +00:00
|
|
|
// references:
|
|
|
|
// - https://github.com/zedeus/nitter/wiki/Instances
|
|
|
|
// - https://status.d420.de/
|
|
|
|
NitterClearnetUrls = []string{
|
2024-07-03 06:42:38 +00:00
|
|
|
"xcancel.com",
|
2023-08-31 08:20:18 +00:00
|
|
|
}
|
2024-02-26 14:43:34 +00:00
|
|
|
|
|
|
|
// since v0.4.0, bot also replaces nostr links with nostr.com so users can pick their client
|
|
|
|
NostrUrlRegexp = regexp.MustCompile(
|
|
|
|
`^(?:https?:\/\/)?(?:www\.)?` +
|
|
|
|
`(?:` +
|
2024-02-26 15:06:15 +00:00
|
|
|
`primal\.net\/(?:e\/)?` +
|
|
|
|
`|snort\.social\/(?:e\/)?` +
|
|
|
|
`|iris\.to\/` +
|
|
|
|
`|highlighter\.com\/(?:a\/)?` +
|
|
|
|
`|nostter\.app\/` +
|
|
|
|
`|coracle\.social\/` +
|
|
|
|
`|satellite\.earth\/` +
|
2024-03-08 07:58:36 +00:00
|
|
|
`|nostrudel\.ninja\/(?:#\/n\/)?` +
|
2024-02-26 14:43:34 +00:00
|
|
|
`)((note|nevent)[a-zA-Z0-9]+)$`)
|
|
|
|
NostrClients = []NostrClient{
|
|
|
|
// list from nostr.com
|
|
|
|
NostrClient{"https://primal.net/e/", "primal.net"},
|
|
|
|
NostrClient{"https://snort.social/e/", "snort.social"},
|
|
|
|
NostrClient{"https://nostrudel.ninja/#/n/", "nostrudel.ninja"},
|
|
|
|
NostrClient{"https://satellite.earth/thread/", "satellite.earth"},
|
|
|
|
NostrClient{"https://coracle.social/", "coracle.social"},
|
|
|
|
NostrClient{"https://nostter.app/", "nostter.app"},
|
|
|
|
NostrClient{"https://highlighter.com/a/", "highlighter.com"},
|
|
|
|
NostrClient{"https://iris.to/", "iris.to"},
|
|
|
|
}
|
2023-08-23 02:06:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func WaitUntilNext(d time.Duration) {
|
|
|
|
now := time.Now()
|
|
|
|
dur := now.Truncate(d).Add(d).Sub(now)
|
|
|
|
log.Println("sleeping for", dur.Round(time.Second))
|
|
|
|
time.Sleep(dur)
|
|
|
|
}
|
|
|
|
|
|
|
|
func CheckNotifications() {
|
|
|
|
var prevHasNewNotes bool
|
|
|
|
for {
|
|
|
|
log.Println("Checking notifications ...")
|
|
|
|
hasNewNotes, err := sn.CheckNotifications()
|
|
|
|
if err != nil {
|
|
|
|
SendToNostr(fmt.Sprint(err))
|
|
|
|
} else {
|
|
|
|
if !prevHasNewNotes && hasNewNotes {
|
|
|
|
// only send on "rising edge"
|
|
|
|
SendToNostr("new notifications")
|
|
|
|
log.Println("Forwarded notifications to monitoring")
|
|
|
|
} else if hasNewNotes {
|
|
|
|
log.Println("Notifications already forwarded")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prevHasNewNotes = hasNewNotes
|
|
|
|
WaitUntilNext(time.Hour)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func SessionKeepAlive() {
|
|
|
|
for {
|
|
|
|
log.Println("Refresh session using GET /api/auth/session ...")
|
|
|
|
sn.RefreshSession()
|
|
|
|
WaitUntilNext(time.Hour)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
go CheckNotifications()
|
|
|
|
go SessionKeepAlive()
|
|
|
|
for {
|
|
|
|
log.Println("fetching items ...")
|
|
|
|
r, err := sn.Items(&sn.ItemsQuery{Sort: "recent", Limit: 21})
|
|
|
|
if err != nil {
|
2023-08-27 21:42:10 +00:00
|
|
|
log.Println(err)
|
2023-08-23 02:06:37 +00:00
|
|
|
SendToNostr(fmt.Sprint(err))
|
|
|
|
WaitUntilNext(time.Minute)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range r.Items {
|
|
|
|
if m := TwitterUrlRegexp.FindStringSubmatch(item.Url); m != nil {
|
|
|
|
log.Printf("item %d is twitter link\n", item.Id)
|
|
|
|
if ItemHasComment(item.Id) {
|
|
|
|
log.Printf("item %d already has nitter links comment\n", item.Id)
|
|
|
|
continue
|
|
|
|
}
|
2023-08-31 17:21:52 +00:00
|
|
|
comment := "**Twitter2Nitter**\n\nClearnet: "
|
2023-08-31 08:20:18 +00:00
|
|
|
for _, nUrl := range NitterClearnetUrls {
|
|
|
|
nitterLink := strings.Replace(item.Url, m[1], nUrl, 1)
|
2023-08-31 17:11:28 +00:00
|
|
|
comment += fmt.Sprintf("[%s](%s) | ", nUrl, nitterLink)
|
2023-08-31 08:20:18 +00:00
|
|
|
}
|
|
|
|
comment = strings.TrimRight(comment, "| ")
|
|
|
|
comment += "\n\n_Nitter is a free and open source alternative Twitter front-end focused on privacy and performance. "
|
2023-08-23 02:06:37 +00:00
|
|
|
comment += "Click [here](https://github.com/zedeus/nitter) for more information._"
|
|
|
|
cId, err := sn.CreateComment(item.Id, comment)
|
|
|
|
if err != nil {
|
2023-08-27 21:42:10 +00:00
|
|
|
log.Println(err)
|
2023-08-23 02:06:37 +00:00
|
|
|
SendToNostr(fmt.Sprint(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
log.Printf("created comment %d\n", cId)
|
|
|
|
SaveComment(&sn.Comment{Id: cId, Text: comment, ParentId: item.Id})
|
|
|
|
} else {
|
|
|
|
log.Printf("item %d is not twitter link\n", item.Id)
|
|
|
|
}
|
2024-02-26 14:43:34 +00:00
|
|
|
if m := NostrUrlRegexp.FindStringSubmatch(item.Url); m != nil {
|
|
|
|
log.Printf("item %d is nostr link\n", item.Id)
|
|
|
|
if ItemHasComment(item.Id) {
|
|
|
|
log.Printf("item %d already has nostr links comment\n", item.Id)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
noteId := m[1]
|
|
|
|
comment := "**Nostr Client Picker**\n\n"
|
|
|
|
for _, client := range NostrClients {
|
|
|
|
comment += fmt.Sprintf("[%s](%s) | ", client.Name, client.Url+noteId)
|
|
|
|
}
|
|
|
|
comment = strings.TrimRight(comment, "| ")
|
|
|
|
cId, err := sn.CreateComment(item.Id, comment)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
SendToNostr(fmt.Sprint(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
log.Printf("created comment %d\n", cId)
|
|
|
|
SaveComment(&sn.Comment{Id: cId, Text: comment, ParentId: item.Id})
|
|
|
|
} else {
|
|
|
|
log.Printf("item %d is not nostr link\n", item.Id)
|
|
|
|
}
|
2023-08-23 02:06:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WaitUntilNext(time.Minute)
|
|
|
|
}
|
|
|
|
}
|