Skip to content

Instantly share code, notes, and snippets.

@Code-Hex
Last active August 1, 2022 11:58
Show Gist options
  • Select an option

  • Save Code-Hex/e1f78e63ebe71ffc1d7b20453c088fab to your computer and use it in GitHub Desktop.

Select an option

Save Code-Hex/e1f78e63ebe71ffc1d7b20453c088fab to your computer and use it in GitHub Desktop.
Slack to discord message migration tool
module github.com/Code-Hex/slack2discord
go 1.18
require (
github.com/Code-Hex/dd v1.1.0 // indirect
github.com/bwmarrin/discordgo v0.25.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/slack-go/slack v0.11.2 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
)
package main
import (
"context"
"fmt"
"html"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/bwmarrin/discordgo"
"github.com/slack-go/slack"
)
func main() {
if err := run(context.Background()); err != nil {
fmt.Fprintf(os.Stderr, "failed to run: %q", err)
os.Exit(1)
}
}
const slackChannel = "C..." // slack channel ID
const discordChannel = "..." // discord channel ID
// < # opening angle bracket
// ([^>\|]+) # link
// (?:\|([^>]+))? # label
// > # closing angle bracket
var re = regexp.MustCompile(`<([^>\|]+)(?:\|([^>]+))?>`)
func run(ctx context.Context) error {
slackClient := slack.New("xoxp-...")
session, err := discordgo.New("...")
if err != nil {
return err
}
messages, err := history(ctx, slackClient)
if err != nil {
return err
}
for _, m := range messages {
fmt.Println("-------", m.Timestamp)
fmt.Println(m.Text)
_, err = session.ChannelMessageSend(discordChannel, m.Text)
if err != nil {
return fmt.Errorf("failed to send message: %w", err)
}
time.Sleep(time.Millisecond * 500)
}
// fmt.Println(dd.Dump(messages))
return nil
}
type Message struct {
Text string
Timestamp string // to use save point
}
func history(ctx context.Context, slackClient *slack.Client) ([]*Message, error) {
var (
cursor string
messages []*Message
)
for i := 0; ; i++ {
resp, err := slackClient.GetConversationHistoryContext(ctx, &slack.GetConversationHistoryParameters{
ChannelID: slackChannel,
Cursor: cursor,
})
if err != nil {
return nil, err
}
for _, message := range resp.Messages {
// If failed, you can continue to specify the timestamp here!
// if message.Timestamp <= "1648392833.619319" {
// continue
// }
text := strings.TrimSpace(message.Text)
if text == "" {
continue
}
if strings.Contains(text, "さんがチャンネルに参加しました") {
continue
}
if strings.Contains(text, "がこのチャンネルの説明を") {
continue
}
replacedText := re.ReplaceAllStringFunc(text, func(s string) string {
parts := re.FindStringSubmatch(s)
link := parts[1]
return link
})
t, err := ts2time(message.Timestamp)
if err != nil {
return nil, err
}
content := fmt.Sprintf("(%s)\n\n%s", t.Format("2006-01-02 15:04:05"), html.UnescapeString(replacedText))
messages = append(messages, &Message{
Text: content,
Timestamp: message.Timestamp,
})
}
if !resp.HasMore {
break
}
cursor = resp.ResponseMetaData.NextCursor
time.Sleep(time.Millisecond * 500)
fmt.Println(i, len(messages), cursor)
}
return reverse(messages), nil
}
var jst *time.Location
var once sync.Once
func getJST() *time.Location {
once.Do(func() {
jst, _ = time.LoadLocation("Asia/Tokyo")
})
return jst
}
func ts2time(ts string) (time.Time, error) {
sep := strings.Split(ts, ".")
if len(sep) != 2 {
return time.Time{}, fmt.Errorf("unexpected ts: %s", ts)
}
sec, ms := sep[0], sep[1]
seci, err := strconv.Atoi(sec)
if err != nil {
return time.Time{}, fmt.Errorf("seci: %w", err)
}
msi, err := strconv.Atoi(ms)
if err != nil {
return time.Time{}, fmt.Errorf("msi: %w", err)
}
return time.Unix(int64(seci), int64(msi)).In(getJST()), nil
}
func reverse(s []*Message) []*Message {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
@Code-Hex

Code-Hex commented Aug 1, 2022

Copy link
Copy Markdown
Author

Get Discord API Token for yours!

Discord の自分のアカウントの Token を取得しよう!

Get Slack API for yours!

You need to get user (xoxp-) token.

スクリーンショット 2022-08-01 20 57 17

User Token Scopes

Scopes that access user data and act on behalf of users that authorize them.

OAuth Scope Description
channels:history View messages and other content in a user’s public channels
im:history View messages and other content in a user’s direct messages

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment