mirror of
https://github.com/appleboy/drone-telegram.git
synced 2026-06-13 18:41:32 +08:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd3806e778 | |||
| 8b2af04235 | |||
| e2fc930461 | |||
| 693f15783c | |||
| b52dcfe9c2 | |||
| 3f25341c8b | |||
| 1142360e53 | |||
| 8c0a2a65dc | |||
| 57468ba50b | |||
| 6cdfb8ba10 | |||
| dfd91c671b | |||
| 78f99c9c7f | |||
| 8bd5e965e7 | |||
| 97227c749e | |||
| fd83eb04f4 | |||
| ee4c70fa29 | |||
| 09796b1872 | |||
| 2e602c4560 | |||
| 75c04eafaa | |||
| 215d08a0d6 | |||
| e370283454 |
+3
-3
@@ -7,7 +7,7 @@ services:
|
||||
go:
|
||||
- 1.5.4
|
||||
- 1.6.3
|
||||
- 1.7.1
|
||||
- 1.7.3
|
||||
- tip
|
||||
|
||||
env:
|
||||
@@ -42,12 +42,12 @@ after_success:
|
||||
- sed -i '/main.go/d' coverage.txt
|
||||
- bash <(curl -s https://codecov.io/bash) -f coverage.txt
|
||||
# deploy from master
|
||||
- if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_GO_VERSION" == "1.7.1" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
|
||||
- if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_GO_VERSION" == "1.7.3" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
|
||||
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
|
||||
make docker_deploy tag=latest;
|
||||
fi
|
||||
# deploy from tag
|
||||
- if [ "$TRAVIS_GO_VERSION" == "1.7.1" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then
|
||||
- if [ "$TRAVIS_GO_VERSION" == "1.7.3" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then
|
||||
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
|
||||
make docker_deploy tag=$TRAVIS_TAG;
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.PHONY:
|
||||
.PHONY: install build test html update docker_build docker_image docker_deploy clean
|
||||
|
||||
VERSION := $(shell git describe --tags || git rev-parse --short HEAD)
|
||||
DEPLOY_ACCOUNT := "appleboy"
|
||||
|
||||
@@ -2,7 +2,19 @@
|
||||
|
||||
[](https://travis-ci.org/appleboy/drone-telegram) [](https://codecov.io/gh/appleboy/drone-telegram) [](https://goreportcard.com/report/github.com/appleboy/drone-telegram)
|
||||
|
||||
Drone plugin for sending telegram notifications.
|
||||
[Drone](https://github.com/drone/drone) plugin for sending telegram notifications.
|
||||
|
||||
## Feature
|
||||
|
||||
* [x] Send with Text Message. (`markdown` or `html` format)
|
||||
* [x] Send with New Photo.
|
||||
* [x] Send with New Document.
|
||||
* [x] Send with New Audio.
|
||||
* [x] Send with New Voice.
|
||||
* [x] Send with New Location.
|
||||
* [x] Send with New Venue.
|
||||
* [x] Send with New Video.
|
||||
* [x] Send with New Sticker.
|
||||
|
||||
## Build
|
||||
|
||||
@@ -45,7 +57,16 @@ docker run --rm \
|
||||
-e PLUGIN_TOKEN=xxxxxxx \
|
||||
-e PLUGIN_TO=xxxxxxx \
|
||||
-e PLUGIN_MESSAGE=test \
|
||||
-e PLUGIN_PHOTO=tests/github.png \
|
||||
-e PLUGIN_DOCUMENT=tests/gophercolor.png \
|
||||
-e PLUGIN_STICKER=tests/github-logo.png \
|
||||
-e PLUGIN_AUDIO=tests/audio.mp3 \
|
||||
-e PLUGIN_VOICE=tests/voice.ogg \
|
||||
-e PLUGIN_LOCATION=24.9163213,121.1424972 \
|
||||
-e PLUGIN_VENUE=24.9163213,121.1424972,title,address \
|
||||
-e PLUGIN_VIDEO=tests/video.mp4 \
|
||||
-e PLUGIN_DEBUG=true \
|
||||
-e PLUGIN_FORMAT=markdown \
|
||||
-e DRONE_REPO_OWNER=appleboy \
|
||||
-e DRONE_REPO_NAME=go-hello \
|
||||
-e DRONE_COMMIT_SHA=e5e82b5eb3737205c25955dcc3dcacc839b7be52 \
|
||||
@@ -54,5 +75,7 @@ docker run --rm \
|
||||
-e DRONE_BUILD_NUMBER=1 \
|
||||
-e DRONE_BUILD_STATUS=success \
|
||||
-e DRONE_BUILD_LINK=http://github.com/appleboy/go-hello \
|
||||
-v $(pwd):$(pwd) \
|
||||
-w $(pwd) \
|
||||
appleboy/drone-telegram
|
||||
```
|
||||
|
||||
Generated
+13
-3
@@ -1,6 +1,16 @@
|
||||
hash: b63ca4eb6e194481209bad2e58f3c4eb2c0ef2b4411ead5b3b7a77677ef815ab
|
||||
updated: 2016-10-04T21:46:25.713693812+08:00
|
||||
hash: 5bdcf53533d043ba1cb6ab55ae6a93aca40c812a20264406bd6e0fe342f51d61
|
||||
updated: 2016-10-31T09:50:34.654875319+08:00
|
||||
imports:
|
||||
- name: github.com/appleboy/drone-facebook
|
||||
version: abda8124c404d424b150cf2a9935e3e108519c6e
|
||||
subpackages:
|
||||
- template
|
||||
- name: github.com/aymerick/raymond
|
||||
version: a2232af10b53ef1ae5a767f5178db3a6c1dab655
|
||||
subpackages:
|
||||
- ast
|
||||
- parser
|
||||
- lexer
|
||||
- name: github.com/joho/godotenv
|
||||
version: 4ed13390c0acd2ff4e371e64d8b97c8954138243
|
||||
subpackages:
|
||||
@@ -8,7 +18,7 @@ imports:
|
||||
- name: github.com/technoweenie/multipartstreamer
|
||||
version: a90a01d73ae432e2611d178c18367fbaa13e0154
|
||||
- name: github.com/urfave/cli
|
||||
version: d53eb991652b1d438abdd34ce4bfa3ef1539108e
|
||||
version: 3eb41f1bb122ac7535cc7b32db6b78b541879726
|
||||
- name: gopkg.in/telegram-bot-api.v4
|
||||
version: a7f48eb2dd301356942677e65bebe0c9aef07013
|
||||
testImports:
|
||||
|
||||
@@ -5,6 +5,10 @@ import:
|
||||
- autoload
|
||||
- package: github.com/urfave/cli
|
||||
- package: gopkg.in/telegram-bot-api.v4
|
||||
- package: github.com/appleboy/drone-facebook
|
||||
version: ^1.3.0
|
||||
subpackages:
|
||||
- template
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
|
||||
@@ -24,7 +24,7 @@ func main() {
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "to",
|
||||
Usage: "send message to user",
|
||||
Usage: "telegram user",
|
||||
EnvVar: "PLUGIN_TO",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
@@ -32,11 +32,57 @@ func main() {
|
||||
Usage: "send telegram message",
|
||||
EnvVar: "PLUGIN_MESSAGE",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "photo",
|
||||
Usage: "send photo message",
|
||||
EnvVar: "PLUGIN_PHOTO",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "document",
|
||||
Usage: "send document message",
|
||||
EnvVar: "PLUGIN_DOCUMENT",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "sticker",
|
||||
Usage: "send sticker message",
|
||||
EnvVar: "PLUGIN_STICKER",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "audio",
|
||||
Usage: "send audio message",
|
||||
EnvVar: "PLUGIN_AUDIO",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "voice",
|
||||
Usage: "send voice message",
|
||||
EnvVar: "PLUGIN_VOICE",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "location",
|
||||
Usage: "send location message",
|
||||
EnvVar: "PLUGIN_LOCATION",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "venue",
|
||||
Usage: "send venue message",
|
||||
EnvVar: "PLUGIN_VENUE",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "video",
|
||||
Usage: "send video message",
|
||||
EnvVar: "PLUGIN_VIDEO",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "enable debug message",
|
||||
EnvVar: "PLUGIN_DEBUG",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Value: "markdown",
|
||||
Usage: "telegram message format",
|
||||
EnvVar: "PLUGIN_FORMAT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.owner",
|
||||
Usage: "repository owner",
|
||||
@@ -90,6 +136,16 @@ func main() {
|
||||
Usage: "build link",
|
||||
EnvVar: "DRONE_BUILD_LINK",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "job.started",
|
||||
Usage: "job started",
|
||||
EnvVar: "DRONE_JOB_STARTED",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "job.finished",
|
||||
Usage: "job finished",
|
||||
EnvVar: "DRONE_JOB_FINISHED",
|
||||
},
|
||||
}
|
||||
app.Run(os.Args)
|
||||
}
|
||||
@@ -101,20 +157,31 @@ func run(c *cli.Context) error {
|
||||
Name: c.String("repo.name"),
|
||||
},
|
||||
Build: Build{
|
||||
Number: c.Int("build.number"),
|
||||
Event: c.String("build.event"),
|
||||
Status: c.String("build.status"),
|
||||
Commit: c.String("commit.sha"),
|
||||
Branch: c.String("commit.branch"),
|
||||
Author: c.String("commit.author"),
|
||||
Message: c.String("commit.message"),
|
||||
Link: c.String("build.link"),
|
||||
Number: c.Int("build.number"),
|
||||
Event: c.String("build.event"),
|
||||
Status: c.String("build.status"),
|
||||
Commit: c.String("commit.sha"),
|
||||
Branch: c.String("commit.branch"),
|
||||
Author: c.String("commit.author"),
|
||||
Message: c.String("commit.message"),
|
||||
Link: c.String("build.link"),
|
||||
Started: c.Float64("job.started"),
|
||||
Finished: c.Float64("job.finished"),
|
||||
},
|
||||
Config: Config{
|
||||
Token: c.String("token"),
|
||||
Debug: c.Bool("debug"),
|
||||
To: c.StringSlice("to"),
|
||||
Message: c.StringSlice("message"),
|
||||
Token: c.String("token"),
|
||||
Debug: c.Bool("debug"),
|
||||
To: c.StringSlice("to"),
|
||||
Message: c.StringSlice("message"),
|
||||
Photo: c.StringSlice("photo"),
|
||||
Document: c.StringSlice("document"),
|
||||
Sticker: c.StringSlice("sticker"),
|
||||
Audio: c.StringSlice("audio"),
|
||||
Voice: c.StringSlice("voice"),
|
||||
Location: c.StringSlice("location"),
|
||||
Video: c.StringSlice("video"),
|
||||
Venue: c.StringSlice("venue"),
|
||||
Format: c.String("format"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/appleboy/drone-facebook/template"
|
||||
"gopkg.in/telegram-bot-api.v4"
|
||||
)
|
||||
|
||||
@@ -19,22 +21,33 @@ type (
|
||||
|
||||
// Build information.
|
||||
Build struct {
|
||||
Event string
|
||||
Number int
|
||||
Commit string
|
||||
Message string
|
||||
Branch string
|
||||
Author string
|
||||
Status string
|
||||
Link string
|
||||
Event string
|
||||
Number int
|
||||
Commit string
|
||||
Message string
|
||||
Branch string
|
||||
Author string
|
||||
Status string
|
||||
Link string
|
||||
Started float64
|
||||
Finished float64
|
||||
}
|
||||
|
||||
// Config for the plugin.
|
||||
Config struct {
|
||||
Token string
|
||||
Debug bool
|
||||
To []string
|
||||
Message []string
|
||||
Token string
|
||||
Debug bool
|
||||
To []string
|
||||
Message []string
|
||||
Photo []string
|
||||
Document []string
|
||||
Sticker []string
|
||||
Audio []string
|
||||
Voice []string
|
||||
Location []string
|
||||
Video []string
|
||||
Venue []string
|
||||
Format string
|
||||
}
|
||||
|
||||
// Plugin values.
|
||||
@@ -43,6 +56,14 @@ type (
|
||||
Build Build
|
||||
Config Config
|
||||
}
|
||||
|
||||
// Location format
|
||||
Location struct {
|
||||
Title string
|
||||
Address string
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
}
|
||||
)
|
||||
|
||||
func trimElement(keys []string) []string {
|
||||
@@ -59,6 +80,60 @@ func trimElement(keys []string) []string {
|
||||
return newKeys
|
||||
}
|
||||
|
||||
func fileExist(keys []string) []string {
|
||||
var newKeys []string
|
||||
|
||||
for _, value := range keys {
|
||||
if _, err := os.Stat(value); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
newKeys = append(newKeys, value)
|
||||
}
|
||||
|
||||
return newKeys
|
||||
}
|
||||
|
||||
func convertLocation(value string) (Location, bool) {
|
||||
var latitude, longitude float64
|
||||
var title, address string
|
||||
var err error
|
||||
values := trimElement(strings.Split(value, ","))
|
||||
|
||||
if len(values) < 2 {
|
||||
return Location{}, true
|
||||
}
|
||||
|
||||
if len(values) > 2 {
|
||||
title = values[2]
|
||||
}
|
||||
|
||||
if len(values) > 3 {
|
||||
title = values[2]
|
||||
address = values[3]
|
||||
}
|
||||
|
||||
latitude, err = strconv.ParseFloat(values[0], 64)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return Location{}, true
|
||||
}
|
||||
|
||||
longitude, err = strconv.ParseFloat(values[1], 64)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return Location{}, true
|
||||
}
|
||||
|
||||
return Location{
|
||||
Title: title,
|
||||
Address: address,
|
||||
Latitude: latitude,
|
||||
Longitude: longitude,
|
||||
}, false
|
||||
}
|
||||
|
||||
func parseID(keys []string) []int64 {
|
||||
var newKeys []int64
|
||||
|
||||
@@ -94,7 +169,7 @@ func (p Plugin) Exec() error {
|
||||
bot, err := tgbotapi.NewBotAPI(p.Config.Token)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
log.Println("Initialize New Bot Error:", err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -103,18 +178,95 @@ func (p Plugin) Exec() error {
|
||||
|
||||
// parse ids
|
||||
ids := parseID(p.Config.To)
|
||||
photos := fileExist(trimElement(p.Config.Photo))
|
||||
documents := fileExist(trimElement(p.Config.Document))
|
||||
stickers := fileExist(trimElement(p.Config.Sticker))
|
||||
audios := fileExist(trimElement(p.Config.Audio))
|
||||
voices := fileExist(trimElement(p.Config.Voice))
|
||||
videos := fileExist(trimElement(p.Config.Video))
|
||||
locations := trimElement(p.Config.Location)
|
||||
venues := trimElement(p.Config.Venue)
|
||||
|
||||
// send message.
|
||||
for _, user := range ids {
|
||||
for _, value := range trimElement(message) {
|
||||
msg := tgbotapi.NewMessage(user, value)
|
||||
bot.Send(msg)
|
||||
txt, err := template.RenderTrim(value, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(user, txt)
|
||||
msg.ParseMode = p.Config.Format
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range photos {
|
||||
msg := tgbotapi.NewPhotoUpload(user, value)
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range documents {
|
||||
msg := tgbotapi.NewDocumentUpload(user, value)
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range stickers {
|
||||
msg := tgbotapi.NewStickerUpload(user, value)
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range audios {
|
||||
msg := tgbotapi.NewAudioUpload(user, value)
|
||||
msg.Title = "Audio Message."
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range voices {
|
||||
msg := tgbotapi.NewVoiceUpload(user, value)
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range videos {
|
||||
msg := tgbotapi.NewVideoUpload(user, value)
|
||||
msg.Caption = "Video Message"
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range locations {
|
||||
location, empty := convertLocation(value)
|
||||
|
||||
if empty == true {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewLocation(user, location.Latitude, location.Longitude)
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
|
||||
for _, value := range venues {
|
||||
location, empty := convertLocation(value)
|
||||
|
||||
if empty == true {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewVenue(user, location.Title, location.Address, location.Latitude, location.Longitude)
|
||||
p.Send(bot, msg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send bot message.
|
||||
func (p Plugin) Send(bot *tgbotapi.BotAPI, msg tgbotapi.Chattable) {
|
||||
_, err := bot.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Message is plugin default message.
|
||||
func (p Plugin) Message(repo Repo, build Build) []string {
|
||||
return []string{fmt.Sprintf("[%s] <%s> (%s)『%s』by %s",
|
||||
|
||||
+79
-4
@@ -66,10 +66,18 @@ func TestSendMessage(t *testing.T) {
|
||||
},
|
||||
|
||||
Config: Config{
|
||||
Token: os.Getenv("TELEGRAM_TOKEN"),
|
||||
To: []string{os.Getenv("TELEGRAM_TO"), "中文ID", "1234567890"},
|
||||
Message: []string{"Test Telegram Chat Bot From Travis or Local", " "},
|
||||
Debug: false,
|
||||
Token: os.Getenv("TELEGRAM_TOKEN"),
|
||||
To: []string{os.Getenv("TELEGRAM_TO"), "中文ID", "1234567890"},
|
||||
Message: []string{"Test Telegram Chat Bot From Travis or Local", "commit message: 『{{ build.message }}』", " "},
|
||||
Photo: []string{"tests/github.png", "1234", " "},
|
||||
Document: []string{"tests/gophercolor.png", "1234", " "},
|
||||
Sticker: []string{"tests/github-logo.png", "tests/github.png", "1234", " "},
|
||||
Audio: []string{"tests/audio.mp3", "1234", " "},
|
||||
Voice: []string{"tests/voice.ogg", "1234", " "},
|
||||
Location: []string{"24.9163213,121.1424972", "1", " "},
|
||||
Venue: []string{"35.661777,139.704051,竹北體育館,新竹縣竹北市", "24.9163213,121.1424972", "1", " "},
|
||||
Video: []string{"tests/video.mp4", "1234", " "},
|
||||
Debug: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -137,3 +145,70 @@ func TestParseID(t *testing.T) {
|
||||
|
||||
assert.Equal(t, result, parseID(input))
|
||||
}
|
||||
|
||||
func TestCheckFileExist(t *testing.T) {
|
||||
var input []string
|
||||
var result []string
|
||||
|
||||
input = []string{"tests/gophercolor.png", "測試", "3"}
|
||||
result = []string{"tests/gophercolor.png"}
|
||||
|
||||
assert.Equal(t, result, fileExist(input))
|
||||
}
|
||||
|
||||
func TestConvertLocation(t *testing.T) {
|
||||
var input string
|
||||
var result Location
|
||||
var empty bool
|
||||
|
||||
input = "1"
|
||||
result, empty = convertLocation(input)
|
||||
|
||||
assert.Equal(t, true, empty)
|
||||
assert.Equal(t, Location{}, result)
|
||||
|
||||
// strconv.ParseInt: parsing "測試": invalid syntax
|
||||
input = "測試,139.704051"
|
||||
result, empty = convertLocation(input)
|
||||
|
||||
assert.Equal(t, true, empty)
|
||||
assert.Equal(t, Location{}, result)
|
||||
|
||||
// strconv.ParseInt: parsing "測試": invalid syntax
|
||||
input = "35.661777,測試"
|
||||
result, empty = convertLocation(input)
|
||||
|
||||
assert.Equal(t, true, empty)
|
||||
assert.Equal(t, Location{}, result)
|
||||
|
||||
input = "35.661777,139.704051"
|
||||
result, empty = convertLocation(input)
|
||||
|
||||
assert.Equal(t, false, empty)
|
||||
assert.Equal(t, Location{
|
||||
Latitude: float64(35.661777),
|
||||
Longitude: float64(139.704051),
|
||||
}, result)
|
||||
|
||||
input = "35.661777,139.704051,title"
|
||||
result, empty = convertLocation(input)
|
||||
|
||||
assert.Equal(t, false, empty)
|
||||
assert.Equal(t, Location{
|
||||
Title: "title",
|
||||
Address: "",
|
||||
Latitude: float64(35.661777),
|
||||
Longitude: float64(139.704051),
|
||||
}, result)
|
||||
|
||||
input = "35.661777,139.704051,title,address"
|
||||
result, empty = convertLocation(input)
|
||||
|
||||
assert.Equal(t, false, empty)
|
||||
assert.Equal(t, Location{
|
||||
Title: "title",
|
||||
Address: "address",
|
||||
Latitude: float64(35.661777),
|
||||
Longitude: float64(139.704051),
|
||||
}, result)
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user