Compare commits
6 Commits
7eb9efe368
...
master
Author | SHA1 | Date | |
---|---|---|---|
956a979204
|
|||
cfd9c7292c
|
|||
7f14289760
|
|||
091f6a455e | |||
d061860fa2 | |||
afc1cee5d7 |
2
Makefile
2
Makefile
@@ -47,7 +47,7 @@ ci:
|
|||||||
|
|
||||||
.PHONY: release # Release the latest version of the application
|
.PHONY: release # Release the latest version of the application
|
||||||
release:
|
release:
|
||||||
@kubectl --namespace tweetsvg set image deployment tweetsvg web=docker.cluster.fun/averagemarcus/tweetsvg:$(SHA)
|
@kubectl --namespace tweetsvg rollout restart deployment tweetsvg
|
||||||
|
|
||||||
.PHONY: help # Show this list of commands
|
.PHONY: help # Show this list of commands
|
||||||
help:
|
help:
|
||||||
|
@@ -1,10 +1,15 @@
|
|||||||
|
# ⚠️ DEPRECATED
|
||||||
|
# Twitter has killed off its free API so this no longer works
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Generate an SVG for a given Tweet ID
|
Generate an SVG for a given Tweet ID
|
||||||
|
|
||||||
Available at https://tweet.cluster.fun/
|
Available at https://tweet.cluster.fun/
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
```
|
```
|
||||||
<img src="https://tweet.cluster.fun/1363048182020792325" />
|
<img src="https://tweet.cluster.fun/1363048182020792325" />
|
||||||
|
5
chars.go
5
chars.go
@@ -74,9 +74,10 @@ var charWidths = map[string]float64{
|
|||||||
"%": 18.416671752929688,
|
"%": 18.416671752929688,
|
||||||
"^": 9.683334350585938,
|
"^": 9.683334350585938,
|
||||||
"*": 9.683334350585938,
|
"*": 9.683334350585938,
|
||||||
"(": 7,
|
"(": 7.8,
|
||||||
")": 7,
|
")": 7.8,
|
||||||
"-": 10.300003051757812,
|
"-": 10.300003051757812,
|
||||||
|
"—": 10.300003051757812,
|
||||||
"_": 9.73333740234375,
|
"_": 9.73333740234375,
|
||||||
"=": 14.333328247070312,
|
"=": 14.333328247070312,
|
||||||
"+": 14.333328247070312,
|
"+": 14.333328247070312,
|
||||||
|
1
go.mod
1
go.mod
@@ -11,6 +11,7 @@ require (
|
|||||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect
|
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect
|
||||||
github.com/grokify/html-strip-tags-go v0.0.1
|
github.com/grokify/html-strip-tags-go v0.0.1
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/rivo/uniseg v0.2.0
|
github.com/rivo/uniseg v0.2.0
|
||||||
github.com/tmdvs/Go-Emoji-Utils v1.1.0
|
github.com/tmdvs/Go-Emoji-Utils v1.1.0
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
||||||
|
2
go.sum
2
go.sum
@@ -14,6 +14,8 @@ github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q
|
|||||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/tmdvs/Go-Emoji-Utils v1.1.0 h1:gtPix7HZPrd49+MNDcuRLvv4xVNxCE5wgjqyuvmbyYg=
|
github.com/tmdvs/Go-Emoji-Utils v1.1.0 h1:gtPix7HZPrd49+MNDcuRLvv4xVNxCE5wgjqyuvmbyYg=
|
||||||
|
12
index.html
12
index.html
@@ -14,8 +14,9 @@
|
|||||||
<meta property="og:description" content="Generate an SVG for a given Tweet ID">
|
<meta property="og:description" content="Generate an SVG for a given Tweet ID">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta property="og:image" content="https://opengraph.cluster.fun/opengraph/?siteTitle=&title=&tags=&image=https%3A%2F%2Fcdn.githubraw.com%2FAverageMarcus%2Ftweetsvg%2Fmaster%2Flogo.png&twitter=Marcus_Noble_&github=AverageMarcus%2Ftweetsvg&website=https%3A%2F%2Ftweet.cluster.fun&bgColor=%23ffffff&fgColor=%23263943">
|
<meta property="og:image" content="https://opengraph.cluster.fun/opengraph/?siteTitle=&title=&tags=&image=https%3A%2F%2Fcdn.githubraw.com%2FAverageMarcus%2Ftweetsvg%2Fmaster%2Flogo.png&twitter=Marcus_Noble_&github=AverageMarcus%2Ftweetsvg&website=https%3A%2F%2Ftweet.cluster.fun&bgColor=%23ffffff&fgColor=%23263943">
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="twitter:creator" content="@Marcus_Noble_" />
|
<meta name="twitter:creator" content="@Marcus_Noble_" />
|
||||||
|
<meta name="twitter:image" content="https://opengraph.cluster.fun/opengraph/?siteTitle=&title=&tags=&image=https%3A%2F%2Fcdn.githubraw.com%2FAverageMarcus%2Ftweetsvg%2Fmaster%2Flogo.png&twitter=Marcus_Noble_&github=AverageMarcus%2Ftweetsvg&website=https%3A%2F%2Ftweet.cluster.fun&bgColor=%23ffffff&fgColor=%23263943">
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
|
||||||
@@ -47,6 +48,11 @@
|
|||||||
figcaption {
|
figcaption {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.announcement {
|
||||||
|
font-size: 3em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -60,6 +66,10 @@
|
|||||||
Generate an SVG for a given Tweet ID
|
Generate an SVG for a given Tweet ID
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
|
<p class="announcement">
|
||||||
|
⚠️ DEPRECATED - Twitter has killed off its free API so this no longer works
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Enter the URL or ID of a tweet to have an SVG generated for it, no JavaScript required!
|
Enter the URL or ID of a tweet to have an SVG generated for it, no JavaScript required!
|
||||||
</p>
|
</p>
|
||||||
|
51
main.go
51
main.go
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/ChimeraCoder/anaconda"
|
"github.com/ChimeraCoder/anaconda"
|
||||||
strip "github.com/grokify/html-strip-tags-go"
|
strip "github.com/grokify/html-strip-tags-go"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
"github.com/rivo/uniseg"
|
"github.com/rivo/uniseg"
|
||||||
emoji "github.com/tmdvs/Go-Emoji-Utils"
|
emoji "github.com/tmdvs/Go-Emoji-Utils"
|
||||||
)
|
)
|
||||||
@@ -36,6 +37,8 @@ var (
|
|||||||
accessTokenSecret string
|
accessTokenSecret string
|
||||||
consumerKey string
|
consumerKey string
|
||||||
consumerSecret string
|
consumerSecret string
|
||||||
|
|
||||||
|
ch *cache.Cache
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -46,6 +49,8 @@ func init() {
|
|||||||
accessTokenSecret = os.Getenv("ACCESS_TOKEN_SECRET")
|
accessTokenSecret = os.Getenv("ACCESS_TOKEN_SECRET")
|
||||||
consumerKey = os.Getenv("CONSUMER_KEY")
|
consumerKey = os.Getenv("CONSUMER_KEY")
|
||||||
consumerSecret = os.Getenv("CONSUMER_SECRET")
|
consumerSecret = os.Getenv("CONSUMER_SECRET")
|
||||||
|
|
||||||
|
ch = cache.New(24*time.Hour, 48*time.Hour)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -78,26 +83,34 @@ func getTweet(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(400)
|
w.WriteHeader(400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tweet, err := api.GetTweet(i, nil)
|
|
||||||
if err != nil {
|
result, found := ch.Get(id)
|
||||||
switch err := err.(type) {
|
if !found {
|
||||||
case *anaconda.ApiError:
|
fmt.Println("No cached tweet found, generating new...")
|
||||||
switch err.Decoded.Errors[0].Code {
|
tweet, err := api.GetTweet(i, nil)
|
||||||
case 63:
|
if err != nil {
|
||||||
fmt.Printf("Generating suspended tweet image for %s\n", id)
|
switch err := err.(type) {
|
||||||
suspendedTweet(w)
|
case *anaconda.ApiError:
|
||||||
return
|
switch err.Decoded.Errors[0].Code {
|
||||||
|
case 63:
|
||||||
|
fmt.Printf("Generating suspended tweet image for %s\n", id)
|
||||||
|
suspendedTweet(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Println(err)
|
||||||
|
w.WriteHeader(404)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
fmt.Println(err)
|
|
||||||
w.WriteHeader(404)
|
processTweet(&tweet)
|
||||||
return
|
|
||||||
|
result = renderTemplate(tweet, false)
|
||||||
|
ch.Set(id, result, cache.DefaultExpiration)
|
||||||
}
|
}
|
||||||
|
|
||||||
processTweet(&tweet)
|
|
||||||
|
|
||||||
w.Header().Set("Content-type", "image/svg+xml")
|
w.Header().Set("Content-type", "image/svg+xml")
|
||||||
_, err = w.Write(renderTemplate(tweet, false))
|
_, err = w.Write(result.([]byte))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
@@ -195,12 +208,20 @@ func calculateHeight(tweet anaconda.Tweet) int64 {
|
|||||||
lineHeight := 28.0
|
lineHeight := 28.0
|
||||||
tweetText := strings.ReplaceAll(tweet.FullText, "<br />", " \n")
|
tweetText := strings.ReplaceAll(tweet.FullText, "<br />", " \n")
|
||||||
tweetText = strip.StripTags(tweetText)
|
tweetText = strip.StripTags(tweetText)
|
||||||
|
tweetText = strings.ReplaceAll(tweetText, "--", "——")
|
||||||
words := regexp.MustCompile(`[ |-]`).Split(tweetText, -1)
|
words := regexp.MustCompile(`[ |-]`).Split(tweetText, -1)
|
||||||
for _, word := range words {
|
for _, word := range words {
|
||||||
|
word = strings.ReplaceAll(word, "——", "--")
|
||||||
if len(emoji.FindAll(word)) > 0 {
|
if len(emoji.FindAll(word)) > 0 {
|
||||||
lineHeight = 32.0
|
lineHeight = 32.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(word, "\n") {
|
||||||
|
height += lineHeight
|
||||||
|
lineWidth = 0
|
||||||
|
word = strings.TrimPrefix(word, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(word, "\n") {
|
if strings.Contains(word, "\n") {
|
||||||
height += lineHeight
|
height += lineHeight
|
||||||
lineHeight = 28.0
|
lineHeight = 28.0
|
||||||
|
Reference in New Issue
Block a user