Compare commits

...

75 Commits

Author SHA1 Message Date
bab3681ac2 Merge pull request 'Merge XMPP reply feature into upstream repo' (#5) from uwaru/matterbridge:master into updatexmppp
Some checks failed
Development / test-build-upload (1.22.x, ubuntu-latest) (push) Waiting to run
Development / golangci-lint (push) Has been cancelled
Reviewed-on: #5
2025-02-23 09:52:49 -08:00
b74b884793
xmpp: Add support for replies
Some checks failed
Development / golangci-lint (pull_request) Has been cancelled
Development / test-build-upload (1.22.x, ubuntu-latest) (pull_request) Has been cancelled
2024-11-04 04:52:16 +01:00
Wim
c4157a4d5b
Update dependencies (#2180)
Some checks failed
Development / golangci-lint (push) Has been cancelled
Development / test-build-upload (1.22.x, ubuntu-latest) (push) Has been cancelled
* Update dependencies

* Fix whatsmeow API changes
2024-08-27 19:04:05 +02:00
Wim
d16645c952
Update mattermost library (#2152)
* Update mattermost library

* Fix linting
2024-05-24 23:08:09 +02:00
Wim
65d78e38af
Update go-xmpp dependency (#2151) 2024-05-24 01:28:32 +02:00
Wim
996e4a7fcf
Update go-xmpp dependency 2024-05-24 01:16:34 +02:00
Wim
a89267943c
Fix linting 2024-05-24 00:31:18 +02:00
Wim
815d8b804f
Fix linting 2024-05-24 00:23:50 +02:00
Brian S. Stephan
6edd5de3b7
Trim nick whitespace when looking up the guild members (discord) (#2072)
this fixes the matching to attempt to find avatars

fixes #2059
2024-05-24 00:19:31 +02:00
Quantum0
2e9db32a83
Fix typos in matterbridge.toml.sample (#2077) 2024-05-24 00:18:27 +02:00
Ben Wiederhake
4bf1c0450c
Split messages if necessary (discord) (#2124)
* Implement and test byte-splitting helper function

* Implement discord botuser message splitting

* Implement discord webhooks message splitting
2024-05-24 00:14:45 +02:00
patrickxia
0bb521512a
Do not bridge CTCP commands other than ACTION (irc) (#2090)
CTCP commands other than ACTION are designed for client-to-client interaction on
IRC networks. Drop such messages when we receive them.

Also get rid of a "CTCP_ACTION" handler in the handler registration.

This

1) can't do anything (if anything, we wanted the string constant
   girc.CTCP_ACTION, which is "ACTION")
2) doesn't do anything in this context, because CTCP handlers are
   registered separately:
   f47717952b/ctcp.go (L205)

The PRIVMSG handler already listens to all CTCPs.

Thanks to @lexande for the bug report (found on a live instance).
2024-05-24 00:05:55 +02:00
Wim
13fd5c5d5b
Fix linting 2024-05-24 00:03:29 +02:00
Ben Wiederhake
d055b4530e
Correctly split three-or-more byte sequences of UTF-8 (#2123) 2024-05-24 00:02:09 +02:00
Neil Hanlon
6b528ffa4f
Update post types and include system removals in skip logic (mattermost) (#2125) 2024-05-23 23:58:58 +02:00
Wim
fa147c076f
Fix linting 2024-05-23 23:57:30 +02:00
Bryan Davis
b2df32bc81
Clear existing IRC event handlers before connecting to new ones (irc) (#2138)
Clear IRC event handlers that we will be registering for the new
connection before registering new handlers. This prevents duplicate
event handlers in the case where we are connecting via a BNC and are
seeing a reconnect. Attempting to clear handlers when none have been set
is a no-op.

Fixes 42wim#1564
Co-authored-by: Andreas Vögele <andreas@andreasvoegele.com>
2024-05-23 23:55:31 +02:00
guangwu
733f4c71b8
Close res body (rocketchat) (#2135) 2024-05-23 23:53:27 +02:00
erentar
70e8c6e9d3
Move deprecated/inactive 3rd party bridges to their own sections (#2143) 2024-05-23 23:50:32 +02:00
Wim
2f33fe86f5
Update dependencies and build to go1.22 (#2113)
* Update dependencies and build to go1.22

* Fix api changes wrt to dependencies

* Update golangci config
2024-05-23 23:44:31 +02:00
Wim
56e7bd01ca
Update dependencies and remove old matterclient lib (#2067) 2023-08-05 20:43:19 +02:00
Juhani Karppinen
9459495484
Fix typo in logging (#2056) 2023-08-05 20:39:46 +02:00
Wim
89b0d362d2
Remove gitter bridge (#2035)
See https://blog.gitter.im/2023/02/13/gitter-has-fully-migrated-to-matrix/
2023-04-05 23:39:15 +02:00
Joseph Mansy
574f25337d
Add nil check for group update (telegram) (#2036) 2023-04-05 23:08:03 +02:00
Joseph Mansy
5bbe422161
Support telegram chat Join/Leave updates (#2019)
Co-authored-by: Wim <wim@42.be>
2023-04-03 23:27:44 +02:00
Faye Duxovni
6500714a93
Don't treat nil as an unknown type of error (zulip) (#2024)
Fixes #1869
2023-04-03 23:27:00 +02:00
Thom Dickson
5feafcddba
Fix broken reply (telegram) (#2026)
Fixes #2021 

* Fix broken reply

* Fix reply/quoting logic with topics

* Update handlers.go

---------

Co-authored-by: Wim <wim@42.be>
2023-04-03 23:20:53 +02:00
mvoolt
3e20a3d180
Create mumble-autolink.tengo (#2029) 2023-04-03 23:00:54 +02:00
dependabot[bot]
60639b6e50
Bump google.golang.org/protobuf from 1.29.0 to 1.29.1 (#2018)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.0 to 1.29.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.0...v1.29.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 22:22:38 +01:00
Joseph Mansy
839f384e45
Return a message ID when sending with a webhook (discord) (#1976)
Resolves #1975
2023-03-14 23:16:22 +01:00
Joseph Mansy
d42277979a
Support topic changes and Join/Leave messages (whatsapp) (#2017) 2023-03-14 23:12:45 +01:00
Wim
24cf007a74
Update matterbridge.toml.sample 2023-03-14 23:05:48 +01:00
Thom Dickson
601f48a50e
Add support for Telegram topics (telegram) (#1942)
Topics are surfaced by appending /<topic-id> to the channel setting for the gateway. 
An example for the topic with ID of 16 would be:

```
[[gateway.inout]]
account="telegram.mytelegram"
channel="-100xxxxxxxxxx/16"
```
2023-03-14 23:03:04 +01:00
Wim
c2b8e298d8
Add extra error checking for fileinfo (api) (#2015) 2023-03-11 23:10:41 +01:00
Asiel Díaz Benítez
0917e17383
Allow to send files in Extra via /api/message (api) (#1993) 2023-03-11 22:34:27 +01:00
Wim
8587fa8585
Fix possible panic in mattermost. (mattermost) Fixes #1947 (#2014) 2023-03-11 18:55:29 +01:00
Wim
f345eeae55
Set correct loglevel (whatsapp). Fixes #1980 (#2013) 2023-03-11 18:18:24 +01:00
Wim
89e2dbac15
Check client disconnect to exit for loop (api). Fixes #1983 (#2012)
Also update to latest melody upstream
2023-03-11 18:14:49 +01:00
Wim
356ada872c
Add handlers at the end. Fixes #1988 (discord) (#2011) 2023-03-11 17:55:11 +01:00
mvoolt
a3deb48726
Add support for Mumble servers with no message length limit (#2008)
* Rename .{jfif,jpe} to .jpg (mumble)

* Fix messages not sending in properly if no limit is set (mumble)

Co-authored-by: yellows111 <ice_ice@email.com>

* Formatting fix (mumble)

---------

Co-authored-by: yellows111 <ice_ice@email.com>
2023-03-10 22:57:35 +01:00
ilmaisin
544cd5cd9e
Fix Android media download problem (whatsapp) (#2010) 2023-03-10 22:30:48 +01:00
Wim
53b63adc71
Update to actions v3 (#2009) 2023-03-10 22:29:44 +01:00
Joseph Mansy
2c349c50c7
Support handling delete message from whatsapp. (#1986) 2023-03-10 22:28:32 +01:00
Wim
08779c2909
Update dependencies (#2007)
* Update dependencies
2023-03-09 22:48:00 +01:00
Wim
d5f9cdf912
Update builds to go 1.20 (#2006) 2023-03-09 22:29:58 +01:00
Joseph Mansy
3a4bdd7c56
Fix rate-overlimit issue when there are more than 5 whatsapp channels in a single account (whatsapp) (#1998)
Resolves #1844
2023-03-09 22:10:09 +01:00
Asiel Díaz Benítez
2b236f3ff7
add deltachat to 3rd party API bridges (#1992) 2023-03-09 22:08:29 +01:00
Joseph Mansy
768fb791c5
Use AuthorSignature as a fallback username (telegram) (#1979)
This comes in handy for annoucement type channels where neither the SenderChat or From structs contain name information.

Also Tweak username logic as when using a full name the username field can be " " instead of "".
2023-03-09 22:02:31 +01:00
Joseph Mansy
d00dcf3f58
Handle Whatsapp threading/replies (whatsapp) (#1974)
* Handle Whatsapp threading/replies.
In this change we are updating whatsapp message IDs to include sender JID as this is necessary to reply to a message
https://github.com/tulir/whatsmeow/issues/88#issuecomment-1093195237
https://github.com/tulir/whatsmeow/discussions/148#discussioncomment-3094325

Based on commit 6afa93e537c53b371db37f35a3546ff0fb669416 from #1934
    Author: Iiro Laiho <iiro.laiho@iki.fi>

* Fix replies.
Sender JID can have a `:` inside of it, using `/` as a delimiter now.
Added messageID parser + struct.
messages sent with an attachment do not show replies
But at least common `sendMessage` will make repies from whatsapp to an attachement bridge across.

The new message ID format broke message deleting, so we change the messageID into the real id at the beginning of send.
We really do need the extra info for when we reply to a message though.

* Refactored message replies.
file/Image/audio/replies all work now.
2023-03-09 21:57:19 +01:00
Ben Wiederhake
d5980303e5
Provide a multi-room multi-bridge example (#1972) 2023-03-09 21:51:11 +01:00
dependabot[bot]
f436ae7b31
Bump golang.org/x/image from 0.3.0 to 0.5.0 (#1973)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.3.0 to 0.5.0.
- [Release notes](https://github.com/golang/image/releases)
- [Commits](https://github.com/golang/image/compare/v0.3.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 21:50:26 +01:00
Joseph Mansy
a9ccc18d73
Prefer mp4 extension for video attachments (whatsapp) (#1971)
resolves #1967
2023-03-09 21:49:38 +01:00
Joseph Mansy
ac681687f8
Handle messages with link preview not being relayed (whatsapp) (#1970)
Resolves #1840
2023-03-09 21:46:57 +01:00
dependabot[bot]
57ce19150f
Bump golang.org/x/net from 0.5.0 to 0.7.0 (#2003)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 21:39:06 +01:00
Wim
24f6747516
Bump version 2023-01-29 18:57:41 +01:00
Wim
6dafebc7cc
Release v1.26.0 (#1957) 2023-01-29 18:26:52 +01:00
Wim
d23f2560d5
Make the cgo lottie a build tag (-tag cgolottie) (#1955)
This should fix #1906 as we don't have any cgo dependencies anymore by
default.
2023-01-29 00:34:27 +01:00
Wim
880586bac4
Update dependencies (#1951) 2023-01-28 22:57:53 +01:00
Wim
eac2a8c8dc
Remove mattermost 5 support (#1936) 2023-01-28 21:57:06 +01:00
ilmaisin
4cf313c4c6
Fix the "Someone" nickname problem (whatsapp) (#1931)
* Apply patch to work around the "Someone" nickname problem

* Code cleanup (whatsapp)

* Cleanup

* Code cleanup (whatsapp)

* Re-examine if sender exists

* Cleanup (whatsapp)
2022-12-14 23:47:30 +01:00
ilmaisin
91353d0a4d
Handle incoming document captions from whatsapp (#1935) 2022-12-14 23:20:00 +01:00
ilmaisin
0a6d64ab48
Improve attachment handling (whatsapp) (#1928)
* Allow captions for document attachments, too

* Handle audio messages

* Improve attachment handling
2022-11-27 00:55:37 +01:00
Wim
4fd0a76727
Update dependencies (#1929) 2022-11-27 00:42:16 +01:00
Sebastian P
6da9d567dc
Implement sending of EventJoinLeave both to and from Mumble (#1915)
* mumble: Implement sending of EventJoinLeave both to and from Mumble (Closes #1435)

* mumble: Break handleUserChange into two functions
2022-11-27 00:17:11 +01:00
Kufat
6d5a3dff22
Allow substitution of bot's nick in RunCommands (irc) (#1890)
* Allow substitution of bot's nick in RunCommands

* Tweak description of "{BOTNICK}"

Made the description of "{BOTNICK}" consistent with that of other keywords
2022-11-27 00:01:10 +01:00
Jair Sanchez
3ad5deaff1
Fix empty messages on IRC (#1897) 2022-11-26 23:53:48 +01:00
Alexander
9bbdf70e69
Fix telegram attachment comment formatting and escaping (#1920) 2022-11-26 23:50:46 +01:00
Lucki
0c83946983
Add Matrix username spoofing (#1875)
* Matrix username spoofing

* Add config sample
2022-09-06 00:46:52 +02:00
Wim
fda05f2262
Update dependencies and fix whatsmeow API changes (#1887)
* Update dependencies

* Fix whatsmau API changes
2022-09-05 21:00:54 +02:00
Buckaroo Banzai
7abf1a5884
Add the beerchat project (a minetest mod) (#1877) 2022-09-05 19:32:30 +02:00
Alexander
365acc36ea
Fix error messages in telegram and slack bridges (#1862)
* Fix message html entities escaping when sending to Telegram

* Fix error messages in telegram and slack bridges

Co-authored-by: Wim <wim@42.be>
2022-09-05 19:31:45 +02:00
Glandos
0482cd191d
Rename freenode to libera in simple config (#1870)
* freenode to libera

The freenode network doesn't really work anymore

* Missing replacement for Libera
2022-08-13 18:51:21 +02:00
Wim
6a3fc71397
Update dependencies and go1.18 (#1873)
* Update dependencies and go1.18

* Exclude unnecessary linters and update build to go1.18
2022-08-13 16:14:26 +02:00
Alexander
3c4192ebf6
Fix message html entities escaping when sending to Telegram (#1855) 2022-07-07 00:47:50 +02:00
Wim
e450e1c447
Bump version 2022-06-25 01:11:49 +02:00
4196 changed files with 4625452 additions and 3044880 deletions

View File

@ -5,28 +5,28 @@ jobs:
name: golangci-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 20
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: "-v --new-from-rev HEAD~5"
args: "-v --new-from-rev HEAD~5 --timeout=5m"
test-build-upload:
strategy:
matrix:
go-version: [1.17.x]
go-version: [1.22.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
stable: false
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Test
@ -39,20 +39,20 @@ jobs:
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
- name: Upload linux 64-bit
if: startsWith(matrix.go-version,'1.17')
uses: actions/upload-artifact@v2
if: startsWith(matrix.go-version,'1.22')
uses: actions/upload-artifact@v3
with:
name: matterbridge-linux-64bit
path: output/lin
- name: Upload windows 64-bit
if: startsWith(matrix.go-version,'1.17')
uses: actions/upload-artifact@v2
if: startsWith(matrix.go-version,'1.22')
uses: actions/upload-artifact@v3
with:
name: matterbridge-windows-64bit
path: output/win
- name: Upload darwin 64-bit
if: startsWith(matrix.go-version,'1.17')
uses: actions/upload-artifact@v2
if: startsWith(matrix.go-version,'1.22')
uses: actions/upload-artifact@v3
with:
name: matterbridge-darwin-64bit
path: output/mac

View File

@ -204,6 +204,17 @@ linters:
- tagliatelle
- errname
- typecheck
- grouper
- decorder
- maintidx
- exhaustruct
- asasalint
- execinquery
- nosnakecase
- exhaustive
- testifylint
- mnd
- depguard
# rules to deal with reported isues
issues:
# List of regexps of issue texts to exclude, empty list by default.

View File

@ -114,19 +114,22 @@ And more...
### 3rd party via matterbridge api
- [Delta Chat](https://github.com/deltachat-bot/matterdelta)
- [Minecraft](https://github.com/raws/mattercraft)
- [Minecraft](https://gitlab.com/Programie/MatterBukkit)
#### Past 3rd party projects
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Facebook messenger](https://github.com/powerjungle/fbridge-asyncio)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Minecraft](https://github.com/elytra/MatterLink)
- [Minecraft](https://github.com/raws/mattercraft)
- [Minecraft](https://gitlab.com/Programie/MatterBukkit)
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
- [MatterAMXX](https://github.com/andrewlindberg/MatterAMXX): [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
- [Ultima Online Emulator](https://github.com/kuoushi/ServUO-Matterbridge)
- [Teamspeak](https://github.com/Archeb/ts-matterbridge)
### API
The API is basic at the moment.
@ -146,6 +149,7 @@ Used by the projects below. Feel free to make a PR to add your project to this l
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
- [ServUO-matterbridge](https://github.com/kuoushi/ServUO-Matterbridge) (A matterbridge connector for ServUO servers)
- [ts-matterbridge](https://github.com/Archeb/ts-matterbridge) (Integrate teamspeak chat with matterbridge)
- [beerchat](https://github.com/mt-mods/beerchat) (Matterbridge link for minetest)
## Chat with us
@ -172,7 +176,7 @@ See <https://github.com/42wim/matterbridge/wiki>
### Binaries
- Latest stable release [v1.25.2](https://github.com/42wim/matterbridge/releases/latest)
- Latest stable release [v1.26.0](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest). On \*nix platforms you may need to make the binary executable - you can do this by running `chmod a+x` on the binary (example: `chmod a+x matterbridge-1.24.1-linux-64bit`). After downloading (and making the binary executable, if necessary), follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
@ -188,7 +192,7 @@ To install or upgrade just download the latest [binary](https://github.com/42wim
Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest)
If you really want to build from source, follow these instructions:
Go 1.17+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
Go 1.18+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
Building the binary with **all** the bridges enabled needs about 3GB RAM to compile.
You can reduce this memory requirement to 0,5GB RAM by adding the `nomsteams` tag if you don't need/use the Microsoft Teams bridge.
@ -399,7 +403,6 @@ Matterbridge wouldn't exist without these libraries:
- discord - <https://github.com/bwmarrin/discordgo>
- echo - <https://github.com/labstack/echo>
- gitter - <https://github.com/sromku/go-gitter>
- gops - <https://github.com/google/gops>
- gozulipbot - <https://github.com/ifo/gozulipbot>
- gumble - <https://github.com/layeh/gumble>

View File

@ -1,17 +1,20 @@
package api
import (
"encoding/base64"
"encoding/json"
"net/http"
"strings"
"sync"
"time"
"gopkg.in/olahol/melody.v1"
"github.com/olahol/melody"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/mitchellh/mapstructure"
ring "github.com/zfjagann/golang-ring"
)
@ -137,6 +140,36 @@ func (b *API) handlePostMessage(c echo.Context) error {
message.Account = b.Account
message.ID = ""
message.Timestamp = time.Now()
var (
fm map[string]interface{}
ds string
ok bool
)
for i, f := range message.Extra["file"] {
fi := config.FileInfo{}
if fm, ok = f.(map[string]interface{}); !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "invalid format for extra")
}
err := mapstructure.Decode(fm, &fi)
if err != nil {
if !strings.Contains(err.Error(), "got string") {
return err
}
}
// mapstructure doesn't decode base64 into []byte, so it must be done manually for fi.Data
if ds, ok = fm["Data"].(string); !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "invalid format for data")
}
data, err := base64.StdEncoding.DecodeString(ds)
if err != nil {
return err
}
fi.Data = &data
message.Extra["file"][i] = fi
}
b.Log.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
b.Remote <- message
return c.JSON(http.StatusOK, message)
@ -166,15 +199,20 @@ func (b *API) handleStream(c echo.Context) error {
}
c.Response().Flush()
for {
select {
// TODO: this causes issues, messages should be broadcasted to all connected clients
msg := b.Messages.Dequeue()
if msg != nil {
if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
return err
default:
msg := b.Messages.Dequeue()
if msg != nil {
if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
return err
}
c.Response().Flush()
}
c.Response().Flush()
time.Sleep(100 * time.Millisecond)
case <-c.Request().Context().Done():
return nil
}
time.Sleep(200 * time.Millisecond)
}
}

View File

@ -121,6 +121,7 @@ type Protocol struct {
MessageLength int // IRC, max length of a message allowed
MessageQueue int // IRC, size of message queue for flood control
MessageSplit bool // IRC, split long messages with newlines on MessageLength instead of clipping
MessageSplitMaxCount int // discord, split long messages into at most this many messages instead of clipping (MessageLength=1950 cannot be configured)
Muc string // xmpp
MxID string // matrix
Name string // all protocols

View File

@ -81,17 +81,6 @@ func (b *Bdiscord) Connect() error {
return err
}
b.Log.Info("Connection succeeded")
b.c.AddHandler(b.messageCreate)
b.c.AddHandler(b.messageTyping)
b.c.AddHandler(b.messageUpdate)
b.c.AddHandler(b.messageDelete)
b.c.AddHandler(b.messageDeleteBulk)
b.c.AddHandler(b.memberAdd)
b.c.AddHandler(b.memberRemove)
b.c.AddHandler(b.memberUpdate)
if b.GetInt("debuglevel") == 1 {
b.c.AddHandler(b.messageEvent)
}
// Add privileged intent for guild member tracking. This is needed to track nicks
// for display names and @mention translation
b.c.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged |
@ -101,7 +90,7 @@ func (b *Bdiscord) Connect() error {
if err != nil {
return err
}
guilds, err := b.c.UserGuilds(100, "", "")
guilds, err := b.c.UserGuilds(100, "", "", false)
if err != nil {
return err
}
@ -233,6 +222,19 @@ func (b *Bdiscord) Connect() error {
b.nickMemberMap[member.Nick] = member
}
}
b.c.AddHandler(b.messageCreate)
b.c.AddHandler(b.messageTyping)
b.c.AddHandler(b.messageUpdate)
b.c.AddHandler(b.messageDelete)
b.c.AddHandler(b.messageDeleteBulk)
b.c.AddHandler(b.memberAdd)
b.c.AddHandler(b.memberRemove)
b.c.AddHandler(b.memberUpdate)
if b.GetInt("debuglevel") == 1 {
b.c.AddHandler(b.messageEvent)
}
return nil
}
@ -314,6 +316,7 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(msg, b.General) {
// TODO: Use ClipOrSplitMessage
rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength, b.GetString("MessageClipped"))
if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil {
b.Log.Errorf("Could not send message %#v: %s", rmsg, err)
@ -325,35 +328,53 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
}
}
msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped"))
msg.Text = b.replaceUserMentions(msg.Text)
// Edit message
if msg.ID != "" {
_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)
return msg.ID, err
}
m := discordgo.MessageSend{
Content: msg.Username + msg.Text,
AllowedMentions: b.getAllowedMentions(),
}
if msg.ParentValid() {
m.Reference = &discordgo.MessageReference{
MessageID: msg.ParentID,
ChannelID: channelID,
GuildID: b.guildID,
// Exploit that a discord message ID is actually just a large number, and we encode a list of IDs by separating them with ";".
msgIds := strings.Split(msg.ID, ";")
msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), len(msgIds))
for len(msgParts) < len(msgIds) {
msgParts = append(msgParts, "((obsoleted by edit))")
}
for i := range msgParts {
// In case of split-messages where some parts remain the same (i.e. only a typo-fix in a huge message), this causes some noop-updates.
// TODO: Optimize away noop-updates of un-edited messages
// TODO: Use RemoteNickFormat instead of this broken concatenation
_, err := b.c.ChannelMessageEdit(channelID, msgIds[i], msg.Username+msgParts[i])
if err != nil {
return "", err
}
}
return msg.ID, nil
}
// Post normal message
res, err := b.c.ChannelMessageSendComplex(channelID, &m)
if err != nil {
return "", err
msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), b.GetInt("MessageSplitMaxCount"))
msgIds := []string{}
for _, msgPart := range msgParts {
m := discordgo.MessageSend{
Content: msg.Username + msgPart,
AllowedMentions: b.getAllowedMentions(),
}
if msg.ParentValid() {
m.Reference = &discordgo.MessageReference{
MessageID: msg.ParentID,
ChannelID: channelID,
GuildID: b.guildID,
}
}
// Post normal message
res, err := b.c.ChannelMessageSendComplex(channelID, &m)
if err != nil {
return "", err
}
msgIds = append(msgIds, res.ID)
}
return res.ID, nil
// Exploit that a discord message ID is actually just a large number, so we encode a list of IDs by separating them with ";".
return strings.Join(msgIds, ";"), nil
}
// handleUploadFile handles native upload of files

View File

@ -68,7 +68,7 @@ func (b *Bdiscord) getGuildMemberByNick(nick string) (*discordgo.Member, error)
b.membersMutex.RLock()
defer b.membersMutex.RUnlock()
if member, ok := b.nickMemberMap[nick]; ok {
if member, ok := b.nickMemberMap[strings.TrimSpace(nick)]; ok {
return member, nil
}
return nil, errors.New("Couldn't find guild member with nick " + nick) // This will most likely get ignored by the caller

View File

@ -2,6 +2,7 @@ package bdiscord
import (
"bytes"
"strings"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
@ -42,12 +43,65 @@ func (b *Bdiscord) maybeGetLocalAvatar(msg *config.Message) string {
return ""
}
func (b *Bdiscord) webhookSendTextOnly(msg *config.Message, channelID string) (string, error) {
msgParts := helper.ClipOrSplitMessage(msg.Text, MessageLength, b.GetString("MessageClipped"), b.GetInt("MessageSplitMaxCount"))
msgIds := []string{}
for _, msgPart := range msgParts {
res, err := b.transmitter.Send(
channelID,
&discordgo.WebhookParams{
Content: msgPart,
Username: msg.Username,
AvatarURL: msg.Avatar,
AllowedMentions: b.getAllowedMentions(),
},
)
if err != nil {
return "", err
} else {
msgIds = append(msgIds, res.ID)
}
}
// Exploit that a discord message ID is actually just a large number, so we encode a list of IDs by separating them with ";".
return strings.Join(msgIds, ";"), nil
}
func (b *Bdiscord) webhookSendFilesOnly(msg *config.Message, channelID string) error {
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo) //nolint:forcetypeassert
file := discordgo.File{
Name: fi.Name,
ContentType: "",
Reader: bytes.NewReader(*fi.Data),
}
content := fi.Comment
// Cannot use the resulting ID for any edits anyway, so throw it away.
// This has to be re-enabled when we implement message deletion.
_, err := b.transmitter.Send(
channelID,
&discordgo.WebhookParams{
Username: msg.Username,
AvatarURL: msg.Avatar,
Files: []*discordgo.File{&file},
Content: content,
AllowedMentions: b.getAllowedMentions(),
},
)
if err != nil {
b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, err)
return err
}
}
return nil
}
// webhookSend send one or more message via webhook, taking care of file
// uploads (from slack, telegram or mattermost).
// Returns messageID and error.
func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) {
func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (string, error) {
var (
res *discordgo.Message
res string
err error
)
@ -60,45 +114,13 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg
// We can't send empty messages.
if msg.Text != "" {
res, err = b.transmitter.Send(
channelID,
&discordgo.WebhookParams{
Content: msg.Text,
Username: msg.Username,
AvatarURL: msg.Avatar,
AllowedMentions: b.getAllowedMentions(),
},
)
if err != nil {
b.Log.Errorf("Could not send text (%s) for message %#v: %s", msg.Text, msg, err)
}
res, err = b.webhookSendTextOnly(msg, channelID)
}
if msg.Extra != nil {
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
file := discordgo.File{
Name: fi.Name,
ContentType: "",
Reader: bytes.NewReader(*fi.Data),
}
content := fi.Comment
_, e2 := b.transmitter.Send(
channelID,
&discordgo.WebhookParams{
Username: msg.Username,
AvatarURL: msg.Avatar,
Files: []*discordgo.File{&file},
Content: content,
AllowedMentions: b.getAllowedMentions(),
},
)
if e2 != nil {
b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, e2)
}
}
if err == nil && msg.Extra != nil {
err = b.webhookSendFilesOnly(msg, channelID)
}
return res, err
}
@ -114,35 +136,44 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st
return "", nil
}
msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped"))
msg.Text = b.replaceUserMentions(msg.Text)
// discord username must be [0..32] max
if len(msg.Username) > 32 {
msg.Username = msg.Username[0:32]
}
if msg.ID != "" {
// Exploit that a discord message ID is actually just a large number, and we encode a list of IDs by separating them with ";".
msgIds := strings.Split(msg.ID, ";")
msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), len(msgIds))
for len(msgParts) < len(msgIds) {
msgParts = append(msgParts, "((obsoleted by edit))")
}
b.Log.Debugf("Editing webhook message")
err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{
Content: msg.Text,
Username: msg.Username,
AllowedMentions: b.getAllowedMentions(),
})
if err == nil {
var editErr error = nil
for i := range msgParts {
// In case of split-messages where some parts remain the same (i.e. only a typo-fix in a huge message), this causes some noop-updates.
// TODO: Optimize away noop-updates of un-edited messages
editErr = b.transmitter.Edit(channelID, msgIds[i], &discordgo.WebhookParams{
Content: msgParts[i],
Username: msg.Username,
AllowedMentions: b.getAllowedMentions(),
})
if editErr != nil {
break
}
}
if editErr == nil {
return msg.ID, nil
}
b.Log.Errorf("Could not edit webhook message: %s", err)
b.Log.Errorf("Could not edit webhook message(s): %s; sending as new message(s) instead", editErr)
}
b.Log.Debugf("Processing webhook sending for message %#v", msg)
discordMsg, err := b.webhookSend(msg, channelID)
msg.Text = b.replaceUserMentions(msg.Text)
msgID, err := b.webhookSend(msg, channelID)
if err != nil {
b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msg, err)
b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msgID, err)
return "", err
}
if discordMsg == nil {
return "", nil
}
return discordMsg.ID, nil
return msgID, nil
}

View File

@ -1,182 +0,0 @@
package bgitter
import (
"fmt"
"strings"
"github.com/42wim/go-gitter"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
)
type Bgitter struct {
c *gitter.Gitter
User *gitter.User
Users []gitter.User
Rooms []gitter.Room
*bridge.Config
}
func New(cfg *bridge.Config) bridge.Bridger {
return &Bgitter{Config: cfg}
}
func (b *Bgitter) Connect() error {
var err error
b.Log.Info("Connecting")
b.c = gitter.New(b.GetString("Token"))
b.User, err = b.c.GetUser()
if err != nil {
return err
}
b.Rooms, err = b.c.GetRooms()
if err != nil {
return err
}
b.Log.Info("Connection succeeded")
return nil
}
func (b *Bgitter) Disconnect() error {
return nil
}
func (b *Bgitter) JoinChannel(channel config.ChannelInfo) error {
roomID, err := b.c.GetRoomId(channel.Name)
if err != nil {
return fmt.Errorf("Could not find roomID for %v. Please create the room on gitter.im", channel.Name)
}
room, err := b.c.GetRoom(roomID)
if err != nil {
return err
}
b.Rooms = append(b.Rooms, *room)
user, err := b.c.GetUser()
if err != nil {
return err
}
_, err = b.c.JoinRoom(roomID, user.ID)
if err != nil {
return err
}
users, _ := b.c.GetUsersInRoom(roomID)
b.Users = append(b.Users, users...)
stream := b.c.Stream(roomID)
go b.c.Listen(stream)
go func(stream *gitter.Stream, room string) {
for event := range stream.Event {
switch ev := event.Data.(type) {
case *gitter.MessageReceived:
// ignore message sent from ourselves
if ev.Message.From.ID != b.User.ID {
b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account)
rmsg := config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room,
Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username), UserID: ev.Message.From.ID,
ID: ev.Message.ID}
if strings.HasPrefix(ev.Message.Text, "@"+ev.Message.From.Username) {
rmsg.Event = config.EventUserAction
rmsg.Text = strings.Replace(rmsg.Text, "@"+ev.Message.From.Username+" ", "", -1)
}
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
case *gitter.GitterConnectionClosed:
b.Log.Errorf("connection with gitter closed for room %s", room)
}
}
}(stream, room.URI)
return nil
}
func (b *Bgitter) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
roomID := b.getRoomID(msg.Channel)
if roomID == "" {
b.Log.Errorf("Could not find roomID for %v", msg.Channel)
return "", nil
}
// Delete message
if msg.Event == config.EventMsgDelete {
if msg.ID == "" {
return "", nil
}
// gitter has no delete message api so we edit message to ""
_, err := b.c.UpdateMessage(roomID, msg.ID, "")
if err != nil {
return "", err
}
return "", nil
}
// Upload a file (in gitter case send the upload URL because gitter has no native upload support)
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
b.c.SendMessage(roomID, rmsg.Username+rmsg.Text)
}
if len(msg.Extra["file"]) > 0 {
return b.handleUploadFile(&msg, roomID)
}
}
// Edit message
if msg.ID != "" {
b.Log.Debugf("updating message with id %s", msg.ID)
_, err := b.c.UpdateMessage(roomID, msg.ID, msg.Username+msg.Text)
if err != nil {
return "", err
}
return "", nil
}
// Post normal message
resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
if err != nil {
return "", err
}
return resp.ID, nil
}
func (b *Bgitter) getRoomID(channel string) string {
for _, v := range b.Rooms {
if v.URI == channel {
return v.ID
}
}
return ""
}
func (b *Bgitter) getAvatar(user string) string {
var avatar string
if b.Users != nil {
for _, u := range b.Users {
if user == u.Username {
return u.AvatarURLSmall
}
}
}
return avatar
}
func (b *Bgitter) handleUploadFile(msg *config.Message, roomID string) (string, error) {
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {
msg.Text += fi.Comment + ": "
}
if fi.URL != "" {
msg.Text = fi.URL
if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL
}
}
_, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
if err != nil {
return "", err
}
}
return "", nil
}

View File

@ -86,6 +86,12 @@ func GetSubLines(message string, maxLineLength int, clippingMessage string) []st
var lines []string
for _, line := range strings.Split(strings.TrimSpace(message), "\n") {
if line == "" {
// Prevent sending empty messages, so we'll skip this line
// if it has no content.
continue
}
if maxLineLength == 0 || len([]byte(line)) <= maxLineLength {
lines = append(lines, line)
continue
@ -205,14 +211,51 @@ func ClipMessage(text string, length int, clippingMessage string) string {
if len(text) > length {
text = text[:length-len(clippingMessage)]
if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
text = text[:len(text)-size]
for len(text) > 0 {
if r, _ := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
text = text[:len(text)-1]
// Note: DecodeLastRuneInString only returns the constant value "1" in
// case of an error. We do not yet know whether the last rune is now
// actually valid. Example: "€" is 0xE2 0x82 0xAC. If we happen to split
// the string just before 0xAC, and go back only one byte, that would
// leave us with a string that ends in the byte 0xE2, which is not a valid
// rune, so we need to try again.
} else {
break
}
}
text += clippingMessage
}
return text
}
func ClipOrSplitMessage(text string, length int, clippingMessage string, splitMax int) []string {
var msgParts []string
remainingText := text
// Invariant of this splitting loop: No text is lost (msgParts+remainingText is the original text),
// and all parts is guaranteed to satisfy the length requirement.
for len(msgParts) < splitMax-1 && len(remainingText) > length {
// Decision: The text needs to be split (again).
var chunk string
wasted := 0
// The longest UTF-8 encoding of a valid rune is 4 bytes (0xF4 0x8F 0xBF 0xBF, encoding U+10FFFF),
// so we should never need to waste 4 or more bytes at a time.
for wasted < 4 && wasted < length {
chunk = remainingText[:length-wasted]
if r, _ := utf8.DecodeLastRuneInString(chunk); r == utf8.RuneError {
wasted += 1
} else {
break
}
}
// Note: At this point, "chunk" might still be invalid, if "text" is very broken.
msgParts = append(msgParts, chunk)
remainingText = remainingText[len(chunk):]
}
msgParts = append(msgParts, ClipMessage(remainingText, length, clippingMessage))
return msgParts
}
// ParseMarkdown takes in an input string as markdown and parses it to html
func ParseMarkdown(input string) string {
extensions := parser.HardLineBreak | parser.NoIntraEmphasis | parser.FencedCode

View File

@ -88,6 +88,15 @@ var lineSplittingTestCases = map[string]struct {
},
nonSplitOutput: []string{"不布人個我此而及單石業喜資富下我河下日沒一我臺空達的常景便物沒為……子大我別名解成?生賣的全直黑,我自我結毛分洲了世當,是政福那是東;斯說"},
},
"Long message, clip three-byte rune after two bytes": {
input: "x 人人生而自由,在尊嚴和權利上一律平等。 他們都具有理性和良知,應該以兄弟情誼的精神對待彼此。",
splitOutput: []string{
"x 人人生而自由,在尊嚴和權利上 <clipped message>",
"一律平等。 他們都具有理性和良知 <clipped message>",
",應該以兄弟情誼的精神對待彼此。",
},
nonSplitOutput: []string{"x 人人生而自由,在尊嚴和權利上一律平等。 他們都具有理性和良知,應該以兄弟情誼的精神對待彼此。"},
},
}
func TestGetSubLines(t *testing.T) {
@ -125,3 +134,105 @@ func TestConvertWebPToPNG(t *testing.T) {
t.Fail()
}
}
var clippingOrSplittingTestCases = map[string]struct {
inputText string
clipSplitLength int
clippingMessage string
splitMax int
expectedOutput []string
}{
"Short single-line message, split 3": {
inputText: "short",
clipSplitLength: 20,
clippingMessage: "?!?!",
splitMax: 3,
expectedOutput: []string{"short"},
},
"Short single-line message, split 1": {
inputText: "short",
clipSplitLength: 20,
clippingMessage: "?!?!",
splitMax: 1,
expectedOutput: []string{"short"},
},
"Short single-line message, split 0": {
// Mainly check that we don't crash.
inputText: "short",
clipSplitLength: 20,
clippingMessage: "?!?!",
splitMax: 0,
expectedOutput: []string{"short"},
},
"Long single-line message, noclip": {
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
clipSplitLength: 50,
clippingMessage: "?!?!",
splitMax: 10,
expectedOutput: []string{
"Lorem ipsum dolor sit amet, consectetur adipiscing",
" elit, sed do eiusmod tempor incididunt ut labore ",
"et dolore magna aliqua.",
},
},
"Long single-line message, noclip tight": {
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
clipSplitLength: 50,
clippingMessage: "?!?!",
splitMax: 3,
expectedOutput: []string{
"Lorem ipsum dolor sit amet, consectetur adipiscing",
" elit, sed do eiusmod tempor incididunt ut labore ",
"et dolore magna aliqua.",
},
},
"Long single-line message, clip custom": {
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
clipSplitLength: 50,
clippingMessage: "?!?!",
splitMax: 2,
expectedOutput: []string{
"Lorem ipsum dolor sit amet, consectetur adipiscing",
" elit, sed do eiusmod tempor incididunt ut lab?!?!",
},
},
"Long single-line message, clip built-in": {
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
clipSplitLength: 50,
clippingMessage: "",
splitMax: 2,
expectedOutput: []string{
"Lorem ipsum dolor sit amet, consectetur adipiscing",
" elit, sed do eiusmod tempor inc <clipped message>",
},
},
"Short multi-line message": {
inputText: "I\ncan't\nget\nno\nsatisfaction!",
clipSplitLength: 50,
clippingMessage: "",
splitMax: 2,
expectedOutput: []string{"I\ncan't\nget\nno\nsatisfaction!"},
},
"Long message containing UTF-8 multi-byte runes": {
inputText: "人人生而自由,在尊嚴和權利上一律平等。 他們都具有理性和良知,應該以兄弟情誼的精神對待彼此。",
clipSplitLength: 50,
clippingMessage: "",
splitMax: 10,
expectedOutput: []string{
"人人生而自由,在尊嚴和權利上一律", // Note: only 48 bytes!
"平等。 他們都具有理性和良知,應該", // Note: only 49 bytes!
"以兄弟情誼的精神對待彼此。",
},
},
}
func TestClipOrSplitMessage(t *testing.T) {
for testname, testcase := range clippingOrSplittingTestCases {
actualOutput := ClipOrSplitMessage(testcase.inputText, testcase.clipSplitLength, testcase.clippingMessage, testcase.splitMax)
assert.Equalf(t, testcase.expectedOutput, actualOutput, "'%s' testcase should give expected lines with clipping+splitting.", testname)
for _, splitLine := range testcase.expectedOutput {
byteLength := len([]byte(splitLine))
assert.True(t, byteLength <= testcase.clipSplitLength, "Splitted line '%s' of testcase '%s' should not exceed the maximum byte-length (%d vs. %d).", splitLine, testname, testcase.clipSplitLength, byteLength)
}
}
}

View File

@ -1,5 +1,4 @@
//go:build cgo
// +build cgo
//go:build cgolottie
package helper

View File

@ -1,4 +1,4 @@
// +build !cgo
//go:build !cgolottie
package helper
@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"github.com/sirupsen/logrus"
)

View File

@ -122,8 +122,18 @@ func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) {
i := b.i
b.Nick = event.Params[0]
b.Log.Debug("Clearing handlers before adding in case of BNC reconnect")
i.Handlers.Clear("PRIVMSG")
i.Handlers.Clear("CTCP_ACTION")
i.Handlers.Clear(girc.RPL_TOPICWHOTIME)
i.Handlers.Clear(girc.NOTICE)
i.Handlers.Clear("JOIN")
i.Handlers.Clear("PART")
i.Handlers.Clear("QUIT")
i.Handlers.Clear("KICK")
i.Handlers.Clear("INVITE")
i.Handlers.AddBg("PRIVMSG", b.handlePrivMsg)
i.Handlers.AddBg("CTCP_ACTION", b.handlePrivMsg)
i.Handlers.Add(girc.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
i.Handlers.AddBg(girc.NOTICE, b.handleNotice)
i.Handlers.AddBg("JOIN", b.handleJoinPart)
@ -195,7 +205,11 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
b.Log.Debugf("== Receiving PRIVMSG: %s %s %#v", event.Source.Name, event.Last(), event)
// set action event
if event.IsAction() {
if ok, ctcp := event.IsCTCP(); ok {
if ctcp.Command != girc.CTCP_ACTION {
b.Log.Debugf("dropping user ctcp, command: %s", ctcp.Command)
return
}
rmsg.Event = config.EventUserAction
}
@ -243,6 +257,7 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
func (b *Birc) handleRunCommands() {
for _, cmd := range b.GetStringSlice("RunCommands") {
cmd = strings.ReplaceAll(cmd, "{BOTNICK}", b.Nick)
if err := b.i.Cmd.SendRaw(cmd); err != nil {
b.Log.Errorf("RunCommands %s failed: %s", cmd, err)
}

View File

@ -51,7 +51,7 @@ func interface2Struct(in interface{}, out interface{}) error {
return json.Unmarshal(jsonObj, out)
}
// getDisplayName retrieves the displayName for mxid, querying the homserver if the mxid is not in the cache.
// getDisplayName retrieves the displayName for mxid, querying the homeserver if the mxid is not in the cache.
func (b *Bmatrix) getDisplayName(mxid string) string {
if b.GetBool("UseUserName") {
return mxid[1:]

View File

@ -148,12 +148,37 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
username := newMatrixUsername(msg.Username)
body := username.plain + msg.Text
formattedBody := username.formatted + helper.ParseMarkdown(msg.Text)
if b.GetBool("SpoofUsername") {
// https://spec.matrix.org/v1.3/client-server-api/#mroommember
type stateMember struct {
AvatarURL string `json:"avatar_url,omitempty"`
DisplayName string `json:"displayname"`
Membership string `json:"membership"`
}
// TODO: reset username afterwards with DisplayName: null ?
m := stateMember{
AvatarURL: "",
DisplayName: username.plain,
Membership: "join",
}
_, err := b.mc.SendStateEvent(channel, "m.room.member", b.UserID, m)
if err == nil {
body = msg.Text
formattedBody = helper.ParseMarkdown(msg.Text)
}
}
// Make a action /me of the message
if msg.Event == config.EventUserAction {
m := matrix.TextMessage{
MsgType: "m.emote",
Body: username.plain + msg.Text,
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
Body: body,
FormattedBody: formattedBody,
Format: "org.matrix.custom.html",
}
@ -224,10 +249,10 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
if msg.ID != "" {
rmsg := EditedMessage{
TextMessage: matrix.TextMessage{
Body: username.plain + msg.Text,
Body: body,
MsgType: "m.text",
Format: "org.matrix.custom.html",
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
FormattedBody: formattedBody,
},
}
@ -266,8 +291,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
if msg.Event == config.EventJoinLeave {
m := matrix.TextMessage{
MsgType: "m.notice",
Body: username.plain + msg.Text,
FormattedBody: username.formatted + msg.Text,
Body: body,
FormattedBody: formattedBody,
Format: "org.matrix.custom.html",
}
@ -297,8 +322,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
m := ReplyMessage{
TextMessage: matrix.TextMessage{
MsgType: "m.text",
Body: username.plain + msg.Text,
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
Body: body,
FormattedBody: formattedBody,
Format: "org.matrix.custom.html",
},
}
@ -338,7 +363,7 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
)
err = b.retry(func() error {
resp, err = b.mc.SendText(channel, username.plain+msg.Text)
resp, err = b.mc.SendText(channel, body)
return err
})
@ -356,8 +381,7 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
)
err = b.retry(func() error {
resp, err = b.mc.SendFormattedText(channel, username.plain+msg.Text,
username.formatted+helper.ParseMarkdown(msg.Text))
resp, err = b.mc.SendFormattedText(channel, body, formattedBody)
return err
})

View File

@ -1,12 +1,12 @@
package bmattermost
import (
"context"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/42wim/matterbridge/matterclient"
matterclient6 "github.com/matterbridge/matterclient"
"github.com/mattermost/mattermost-server/v5/model"
model6 "github.com/mattermost/mattermost-server/v6/model"
"github.com/matterbridge/matterclient"
"github.com/mattermost/mattermost/server/public/model"
)
// handleDownloadAvatar downloads the avatar of userid from channel
@ -26,20 +26,11 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
var (
data []byte
err error
resp *model.Response
)
if b.mc6 != nil {
data, _, err = b.mc6.Client.GetProfileImage(userid, "")
if err != nil {
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
return
}
} else {
data, resp = b.mc.Client.GetProfileImage(userid, "")
if resp.Error != nil {
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
return
}
data, _, err = b.mc.Client.GetProfileImage(context.TODO(), userid, "")
if err != nil {
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
return
}
err = helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
@ -52,33 +43,10 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
}
}
// handleDownloadFile handles file download
//nolint:wrapcheck
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
if b.mc6 != nil {
return b.handleDownloadFile6(rmsg, id)
}
url, _ := b.mc.Client.GetFileLink(id)
finfo, resp := b.mc.Client.GetFileInfo(id)
if resp.Error != nil {
return resp.Error
}
err := helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
if err != nil {
return err
}
data, resp := b.mc.Client.DownloadFile(id, true)
if resp.Error != nil {
return resp.Error
}
helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
return nil
}
// nolint:wrapcheck
func (b *Bmattermost) handleDownloadFile6(rmsg *config.Message, id string) error {
url, _, _ := b.mc6.Client.GetFileLink(id)
finfo, _, err := b.mc6.Client.GetFileInfo(id)
url, _, _ := b.mc.Client.GetFileLink(context.TODO(), id)
finfo, _, err := b.mc.Client.GetFileInfo(context.TODO(), id)
if err != nil {
return err
}
@ -86,7 +54,7 @@ func (b *Bmattermost) handleDownloadFile6(rmsg *config.Message, id string) error
if err != nil {
return err
}
data, _, err := b.mc6.Client.DownloadFile(id, true)
data, _, err := b.mc.Client.DownloadFile(context.TODO(), id, true)
if err != nil {
return err
}
@ -125,15 +93,10 @@ func (b *Bmattermost) handleMatter() {
}
}
//nolint:cyclop
func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
if b.mc6 != nil {
b.Log.Debug("starting matterclient6")
b.handleMatterClient6(messages)
return
}
for message := range b.mc.MessageChan {
b.Log.Debugf("%#v", message.Raw.Data)
b.Log.Debugf("%#v %#v", message.Raw.GetData(), message.Raw.EventType())
if b.skipMessage(message) {
b.Log.Debugf("Skipped message: %#v", message)
@ -166,11 +129,11 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
b.handleProps(rmsg, message)
// create a text for bridges that don't support native editing
if message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED && !b.GetBool("EditDisable") {
if message.Raw.EventType() == model.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
rmsg.Text = message.Text + b.GetString("EditSuffix")
}
if message.Raw.Event == model.WEBSOCKET_EVENT_POST_DELETED {
if message.Raw.EventType() == model.WebsocketEventPostDeleted {
rmsg.Event = config.EventMsgDelete
}
@ -192,68 +155,6 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
}
}
// nolint:cyclop
func (b *Bmattermost) handleMatterClient6(messages chan *config.Message) {
for message := range b.mc6.MessageChan {
b.Log.Debugf("%#v %#v", message.Raw.GetData(), message.Raw.EventType())
if b.skipMessage6(message) {
b.Log.Debugf("Skipped message: %#v", message)
continue
}
channelName := b.getChannelName(message.Post.ChannelId)
if channelName == "" {
channelName = message.Channel
}
// only download avatars if we have a place to upload them (configured mediaserver)
if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
b.handleDownloadAvatar(message.UserID, channelName)
}
b.Log.Debugf("== Receiving event %#v", message)
rmsg := &config.Message{
Username: message.Username,
UserID: message.UserID,
Channel: channelName,
Text: message.Text,
ID: message.Post.Id,
ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
Extra: make(map[string][]interface{}),
}
// handle mattermost post properties (override username and attachments)
b.handleProps6(rmsg, message)
// create a text for bridges that don't support native editing
if message.Raw.EventType() == model6.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
rmsg.Text = message.Text + b.GetString("EditSuffix")
}
if message.Raw.EventType() == model6.WebsocketEventPostDeleted {
rmsg.Event = config.EventMsgDelete
}
for _, id := range message.Post.FileIds {
err := b.handleDownloadFile(rmsg, id)
if err != nil {
b.Log.Errorf("download failed: %s", err)
}
}
// Use nickname instead of username if defined
if !b.GetBool("useusername") {
if nick := b.mc6.GetNickName(rmsg.UserID); nick != "" {
rmsg.Username = nick
}
}
messages <- rmsg
}
}
func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
for {
message := b.mh.Receive()
@ -268,12 +169,7 @@ func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
}
}
// handleUploadFile handles native upload of files
func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
if b.mc6 != nil {
return b.handleUploadFile6(msg)
}
var err error
var res, id string
channelID := b.getChannelID(msg.Channel)
@ -292,53 +188,8 @@ func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
return res, err
}
// nolint:forcetypeassert,wrapcheck
func (b *Bmattermost) handleUploadFile6(msg *config.Message) (string, error) {
var err error
var res, id string
channelID := b.getChannelID(msg.Channel)
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
id, err = b.mc6.UploadFile(*fi.Data, channelID, fi.Name)
if err != nil {
return "", err
}
msg.Text = fi.Comment
if b.GetBool("PrefixMessagesWithNick") {
msg.Text = msg.Username + msg.Text
}
res, err = b.mc6.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
}
return res, err
}
//nolint:forcetypeassert
func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Message) {
props := message.Post.Props
if props == nil {
return
}
if _, ok := props["override_username"].(string); ok {
rmsg.Username = props["override_username"].(string)
}
if _, ok := props["attachments"].([]interface{}); ok {
rmsg.Extra["attachments"] = props["attachments"].([]interface{})
if rmsg.Text == "" {
for _, attachment := range rmsg.Extra["attachments"] {
attach := attachment.(map[string]interface{})
if attach["text"].(string) != "" {
rmsg.Text += attach["text"].(string)
continue
}
if attach["fallback"].(string) != "" {
rmsg.Text += attach["fallback"].(string)
}
}
}
}
}
// nolint:forcetypeassert
func (b *Bmattermost) handleProps6(rmsg *config.Message, message *matterclient6.Message) {
props := message.Post.Props
if props == nil {
return

View File

@ -6,11 +6,9 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/42wim/matterbridge/matterclient"
"github.com/42wim/matterbridge/matterhook"
matterclient6 "github.com/matterbridge/matterclient"
"github.com/mattermost/mattermost-server/v5/model"
model6 "github.com/mattermost/mattermost-server/v6/model"
"github.com/matterbridge/matterclient"
"github.com/mattermost/mattermost/server/public/model"
)
func (b *Bmattermost) doConnectWebhookBind() error {
@ -24,33 +22,15 @@ func (b *Bmattermost) doConnectWebhookBind() error {
})
case b.GetString("Token") != "":
b.Log.Info("Connecting using token (sending)")
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
if b.v6 {
err := b.apiLogin6()
if err != nil {
return err
}
} else {
err := b.apiLogin()
if err != nil {
return err
}
err := b.apiLogin()
if err != nil {
return err
}
case b.GetString("Login") != "":
b.Log.Info("Connecting using login/password (sending)")
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
if b.v6 {
err := b.apiLogin6()
if err != nil {
return err
}
} else {
err := b.apiLogin()
if err != nil {
return err
}
err := b.apiLogin()
if err != nil {
return err
}
default:
b.Log.Info("Connecting using webhookbindaddress (receiving)")
@ -72,45 +52,28 @@ func (b *Bmattermost) doConnectWebhookURL() error {
})
if b.GetString("Token") != "" {
b.Log.Info("Connecting using token (receiving)")
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
if b.v6 {
err := b.apiLogin6()
if err != nil {
return err
}
} else {
err := b.apiLogin()
if err != nil {
return err
}
err := b.apiLogin()
if err != nil {
return err
}
} else if b.GetString("Login") != "" {
b.Log.Info("Connecting using login/password (receiving)")
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
if b.v6 {
err := b.apiLogin6()
if err != nil {
return err
}
} else {
err := b.apiLogin()
if err != nil {
return err
}
err := b.apiLogin()
if err != nil {
return err
}
}
return nil
}
//nolint:wrapcheck
func (b *Bmattermost) apiLogin() error {
password := b.GetString("Password")
if b.GetString("Token") != "" {
password = "token=" + b.GetString("Token")
}
b.mc = matterclient.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"))
b.mc = matterclient.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"), "")
if b.GetBool("debug") {
b.mc.SetLogLevel("debug")
}
@ -118,39 +81,13 @@ func (b *Bmattermost) apiLogin() error {
b.mc.SkipVersionCheck = b.GetBool("SkipVersionCheck")
b.mc.NoTLS = b.GetBool("NoTLS")
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
err := b.mc.Login()
if err != nil {
return err
}
b.Log.Info("Connection succeeded")
b.TeamID = b.mc.GetTeamId()
go b.mc.WsReceiver()
go b.mc.StatusLoop()
return nil
}
// nolint:wrapcheck
func (b *Bmattermost) apiLogin6() error {
password := b.GetString("Password")
if b.GetString("Token") != "" {
password = "token=" + b.GetString("Token")
}
b.mc6 = matterclient6.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"), "")
if b.GetBool("debug") {
b.mc6.SetLogLevel("debug")
}
b.mc6.SkipTLSVerify = b.GetBool("SkipTLSVerify")
b.mc6.SkipVersionCheck = b.GetBool("SkipVersionCheck")
b.mc6.NoTLS = b.GetBool("NoTLS")
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
if err := b.mc6.Login(); err != nil {
if err := b.mc.Login(); err != nil {
return err
}
b.Log.Info("Connection succeeded")
b.TeamID = b.mc6.GetTeamID()
b.TeamID = b.mc.GetTeamID()
return nil
}
@ -234,11 +171,23 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {
}
// skipMessages returns true if this message should not be handled
//
//nolint:gocyclo,cyclop
func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
// Handle join/leave
if message.Type == "system_join_leave" ||
message.Type == "system_join_channel" ||
message.Type == "system_leave_channel" {
skipJoinMessageTypes := map[string]struct{}{
"system_join_leave": {}, // deprecated for system_add_to_channel
"system_leave_channel": {}, // deprecated for system_remove_from_channel
"system_join_channel": {},
"system_add_to_channel": {},
"system_remove_from_channel": {},
"system_add_to_team": {},
"system_remove_from_team": {},
}
// dirty hack to efficiently check if this element is in the map without writing a contains func
// can be replaced with native slice.contains with go 1.21
if _, ok := skipJoinMessageTypes[message.Type]; ok {
if b.GetBool("nosendjoinpart") {
return true
}
@ -260,76 +209,7 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
}
// Handle edited messages
if (message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED) && b.GetBool("EditDisable") {
return true
}
// Ignore non-post messages
if message.Post == nil {
b.Log.Debugf("ignoring nil message.Post: %#v", message)
return true
}
// Ignore messages sent from matterbridge
if message.Post.Props != nil {
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {
b.Log.Debugf("sent by matterbridge, ignoring")
return true
}
}
// Ignore messages sent from a user logged in as the bot
if b.mc.User.Username == message.Username {
return true
}
// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
if message.Post.HasReactions {
return true
}
// ignore messages from other teams than ours
if message.Raw.Data["team_id"].(string) != b.TeamID {
return true
}
// only handle posted, edited or deleted events
if !(message.Raw.Event == "posted" || message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED ||
message.Raw.Event == model.WEBSOCKET_EVENT_POST_DELETED) {
return true
}
return false
}
// skipMessages returns true if this message should not be handled
// nolint:gocyclo,cyclop
func (b *Bmattermost) skipMessage6(message *matterclient6.Message) bool {
// Handle join/leave
if message.Type == "system_join_leave" ||
message.Type == "system_join_channel" ||
message.Type == "system_leave_channel" {
if b.GetBool("nosendjoinpart") {
return true
}
channelName := b.getChannelName(message.Post.ChannelId)
if channelName == "" {
channelName = message.Channel
}
b.Log.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
b.Remote <- config.Message{
Username: "system",
Text: message.Text,
Channel: channelName,
Account: b.Account,
Event: config.EventJoinLeave,
}
return true
}
// Handle edited messages
if (message.Raw.EventType() == model6.WebsocketEventPostEdited) && b.GetBool("EditDisable") {
if (message.Raw.EventType() == model.WebsocketEventPostEdited) && b.GetBool("EditDisable") {
return true
}
@ -348,7 +228,7 @@ func (b *Bmattermost) skipMessage6(message *matterclient6.Message) bool {
}
// Ignore messages sent from a user logged in as the bot
if b.mc6.User.Username == message.Username {
if b.mc.User.Username == message.Username {
b.Log.Debug("message from same user as bot, ignoring")
return true
}
@ -365,8 +245,8 @@ func (b *Bmattermost) skipMessage6(message *matterclient6.Message) bool {
}
// only handle posted, edited or deleted events
if !(message.Raw.EventType() == "posted" || message.Raw.EventType() == model6.WebsocketEventPostEdited ||
message.Raw.EventType() == model6.WebsocketEventPostDeleted) {
if !(message.Raw.EventType() == "posted" || message.Raw.EventType() == model.WebsocketEventPostEdited ||
message.Raw.EventType() == model.WebsocketEventPostDeleted) {
return true
}
return false
@ -396,11 +276,7 @@ func (b *Bmattermost) getChannelID(name string) string {
return idcheck[1]
}
if b.mc6 != nil {
return b.mc6.GetChannelID(name, b.TeamID)
}
return b.mc.GetChannelId(name, b.TeamID)
return b.mc.GetChannelID(name, b.TeamID)
}
func (b *Bmattermost) getChannelName(id string) string {

View File

@ -1,6 +1,7 @@
package bmattermost
import (
"context"
"errors"
"fmt"
"strings"
@ -9,16 +10,14 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/42wim/matterbridge/matterclient"
"github.com/42wim/matterbridge/matterhook"
matterclient6 "github.com/matterbridge/matterclient"
"github.com/matterbridge/matterclient"
"github.com/rs/xid"
)
type Bmattermost struct {
mh *matterhook.Client
mc *matterclient.MMClient
mc6 *matterclient6.Client
mc *matterclient.Client
v6 bool
uuid string
TeamID string
@ -74,34 +73,17 @@ func (b *Bmattermost) Connect() error {
return nil
case b.GetString("Token") != "":
b.Log.Info("Connecting using token (sending and receiving)")
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
if b.v6 {
err := b.apiLogin6()
if err != nil {
return err
}
} else {
err := b.apiLogin()
if err != nil {
return err
}
err := b.apiLogin()
if err != nil {
return err
}
go b.handleMatter()
case b.GetString("Login") != "":
b.Log.Info("Connecting using login/password (sending and receiving)")
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
if b.v6 {
err := b.apiLogin6()
if err != nil {
return err
}
} else {
err := b.apiLogin()
if err != nil {
return err
}
err := b.apiLogin()
if err != nil {
return err
}
go b.handleMatter()
}
@ -132,10 +114,6 @@ func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
return fmt.Errorf("Could not find channel ID for channel %s", channel.Name)
}
if b.mc6 != nil {
return b.mc6.JoinChannel(id) // nolint:wrapcheck
}
return b.mc.JoinChannel(id)
}
@ -168,9 +146,6 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
if msg.ID == "" {
return "", nil
}
if b.mc6 != nil {
return msg.ID, b.mc6.DeleteMessage(msg.ID) // nolint:wrapcheck
}
return msg.ID, b.mc.DeleteMessage(msg.ID)
}
@ -183,36 +158,20 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
if msg.ParentID != "" {
if b.mc6 != nil {
post, _, err := b.mc6.Client.GetPost(msg.ParentID, "")
if err != nil {
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
}
if post.RootId != "" {
msg.ParentID = post.RootId
}
} else {
post, res := b.mc.Client.GetPost(msg.ParentID, "")
if res.Error != nil {
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, res.Error.DetailedError)
}
if post.RootId != "" {
msg.ParentID = post.RootId
}
post, _, err := b.mc.Client.GetPost(context.TODO(), msg.ParentID, "")
if err != nil {
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
}
if post != nil && post.RootId != "" {
msg.ParentID = post.RootId
}
}
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
if b.mc6 != nil {
if _, err := b.mc6.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
b.Log.Errorf("PostMessage failed: %s", err)
}
} else {
if _, err := b.mc.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
b.Log.Errorf("PostMessage failed: %s", err)
}
if _, err := b.mc.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
b.Log.Errorf("PostMessage failed: %s", err)
}
}
if len(msg.Extra["file"]) > 0 {
@ -227,17 +186,9 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
// Edit message if we have an ID
if msg.ID != "" {
if b.mc6 != nil {
return b.mc6.EditMessage(msg.ID, msg.Text) // nolint:wrapcheck
}
return b.mc.EditMessage(msg.ID, msg.Text)
}
// Post normal message
if b.mc6 != nil {
return b.mc6.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID) // nolint:wrapcheck
}
return b.mc.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID)
}

View File

@ -42,7 +42,14 @@ func (b *Bmumble) handleTextMessage(event *gumble.TextMessageEvent) {
if part.Image == nil {
rmsg.Text = part.Text
} else {
fname := b.Account + "_" + strconv.FormatInt(now.UnixNano(), 10) + "_" + strconv.Itoa(i) + part.FileExtension
fileExt := part.FileExtension
if fileExt == ".jfif" {
fileExt = ".jpg"
}
if fileExt == ".jpe" {
fileExt = ".jpg"
}
fname := b.Account + "_" + strconv.FormatInt(now.UnixNano(), 10) + "_" + strconv.Itoa(i) + fileExt
rmsg.Extra = make(map[string][]interface{})
if err = helper.HandleDownloadSize(b.Log, &rmsg, fname, int64(len(part.Image)), b.General); err != nil {
b.Log.WithError(err).Warn("not including image in message")
@ -62,7 +69,6 @@ func (b *Bmumble) handleConnect(event *gumble.ConnectEvent) {
}
// No need to talk or listen
event.Client.Self.SetSelfDeafened(true)
event.Client.Self.SetSelfMuted(true)
// if the Channel variable is set, this is a reconnect -> rejoin channel
if b.Channel != nil {
if err := b.doJoin(event.Client, *b.Channel); err != nil {
@ -78,19 +84,75 @@ func (b *Bmumble) handleConnect(event *gumble.ConnectEvent) {
}
}
func (b *Bmumble) handleUserChange(event *gumble.UserChangeEvent) {
// Only care about changes to self
if event.User != event.Client.Self {
func (b *Bmumble) handleJoinLeave(event *gumble.UserChangeEvent) {
// Ignore events happening before setup is done
if b.Channel == nil {
return
}
// Someone attempted to move the user out of the configured channel; attempt to join back
if b.Channel != nil {
if b.GetBool("nosendjoinpart") {
return
}
b.Log.Debugf("Received gumble user change event: %+v", event)
text := ""
switch {
case event.Type&gumble.UserChangeKicked > 0:
text = " was kicked"
case event.Type&gumble.UserChangeBanned > 0:
text = " was banned"
case event.Type&gumble.UserChangeDisconnected > 0:
if event.User.Channel != nil && event.User.Channel.ID == *b.Channel {
text = " left"
}
case event.Type&gumble.UserChangeConnected > 0:
if event.User.Channel != nil && event.User.Channel.ID == *b.Channel {
text = " joined"
}
case event.Type&gumble.UserChangeChannel > 0:
// Treat Mumble channel changes the same as connects/disconnects; as far as matterbridge is concerned, they are identical
if event.User.Channel != nil && event.User.Channel.ID == *b.Channel {
text = " joined"
} else {
text = " left"
}
}
if text != "" {
b.Remote <- config.Message{
Username: "system",
Text: event.User.Name + text,
Channel: strconv.FormatUint(uint64(*b.Channel), 10),
Account: b.Account,
Event: config.EventJoinLeave,
}
}
}
func (b *Bmumble) handleUserModified(event *gumble.UserChangeEvent) {
// Ignore events happening before setup is done
if b.Channel == nil {
return
}
if event.Type&gumble.UserChangeChannel > 0 {
// Someone attempted to move the user out of the configured channel; attempt to join back
if err := b.doJoin(event.Client, *b.Channel); err != nil {
b.Log.Error(err)
}
}
}
func (b *Bmumble) handleUserChange(event *gumble.UserChangeEvent) {
// The UserChangeEvent is used for both the gumble client itself as well as other clients
if event.User != event.Client.Self {
// other users
b.handleJoinLeave(event)
} else {
// gumble user
b.handleUserModified(event)
}
}
func (b *Bmumble) handleDisconnect(event *gumble.DisconnectEvent) {
b.connected <- *event
}

View File

@ -93,7 +93,7 @@ func (b *Bmumble) JoinChannel(channel config.ChannelInfo) error {
func (b *Bmumble) Send(msg config.Message) (string, error) {
// Only process text messages
b.Log.Debugf("=> Received local message %#v", msg)
if msg.Event != "" && msg.Event != config.EventUserAction {
if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave {
return "", nil
}
@ -250,7 +250,12 @@ func (b *Bmumble) processMessage(msg *config.Message) {
// If there is a maximum message length, split and truncate the lines
var msgLines []string
if maxLength := b.serverConfig.MaximumMessageLength; maxLength != nil {
msgLines = helper.GetSubLines(msg.Text, *maxLength-len(msg.Username), b.GetString("MessageClipped"))
if *maxLength != 0 { // Some servers will have unlimited message lengths.
// Not doing this makes underflows happen.
msgLines = helper.GetSubLines(msg.Text, *maxLength-len(msg.Username), b.GetString("MessageClipped"))
} else {
msgLines = helper.GetSubLines(msg.Text, 0, b.GetString("MessageClipped"))
}
} else {
msgLines = helper.GetSubLines(msg.Text, 0, b.GetString("MessageClipped"))
}

View File

@ -135,6 +135,7 @@ func (b *Brocketchat) uploadFile(fi *config.FileInfo, channel string) error {
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err

View File

@ -80,7 +80,7 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
case *slack.FileDeletedEvent:
rmsg, err := b.handleFileDeletedEvent(ev)
if err != nil {
b.Log.Errorf("%#v", err)
b.Log.Printf("%#v", err)
continue
}
messages <- rmsg

View File

@ -101,7 +101,9 @@ func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config
var err error
var bot *slack.Bot
for {
bot, err = b.rtm.GetBotInfo(ev.BotID)
bot, err = b.rtm.GetBotInfo(slack.GetBotInfoParameters{
Bot: ev.BotID,
})
if err == nil {
break
}

View File

@ -11,7 +11,7 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/davecgh/go-spew/spew"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
tgbotapi "github.com/matterbridge/telegram-bot-api/v6"
)
func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *tgbotapi.Message) *tgbotapi.Message {
@ -20,6 +20,11 @@ func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *
if posted.Text == "/chatId" {
chatID := strconv.FormatInt(posted.Chat.ID, 10)
// Handle chat topics
if posted.IsTopicMessage {
chatID = chatID + "/" + strconv.Itoa(posted.MessageThreadID)
}
_, err := b.Send(config.Message{
Channel: chatID,
Text: fmt.Sprintf("ID of this chat: %s", chatID),
@ -91,7 +96,8 @@ func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Mess
// handleQuoting handles quoting of previous messages
func (b *Btelegram) handleQuoting(rmsg *config.Message, message *tgbotapi.Message) {
if message.ReplyToMessage != nil {
// Used to check if the message was a reply to the root topic
if message.ReplyToMessage != nil && (!message.IsTopicMessage || message.ReplyToMessage.MessageID != message.MessageThreadID) { //nolint:nestif
usernameReply := ""
if message.ReplyToMessage.From != nil {
if b.GetBool("UseFirstName") {
@ -128,7 +134,9 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
rmsg.Username = message.From.FirstName
}
if b.GetBool("UseFullName") {
rmsg.Username = message.From.FirstName + " " + message.From.LastName
if message.From.FirstName != "" && message.From.LastName != "" {
rmsg.Username = message.From.FirstName + " " + message.From.LastName
}
}
if rmsg.Username == "" {
rmsg.Username = message.From.UserName
@ -148,7 +156,9 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
rmsg.Username = message.SenderChat.FirstName
}
if b.GetBool("UseFullName") {
rmsg.Username = message.SenderChat.FirstName + " " + message.SenderChat.LastName
if message.SenderChat.FirstName != "" && message.SenderChat.LastName != "" {
rmsg.Username = message.SenderChat.FirstName + " " + message.SenderChat.LastName
}
}
if rmsg.Username == "" || rmsg.Username == "Channel_Bot" {
@ -164,6 +174,11 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
}
}
// Fallback on author signature (used in "channel" type of chat)
if rmsg.Username == "" && message.AuthorSignature != "" {
rmsg.Username = message.AuthorSignature
}
// if we really didn't find a username, set it to unknown
if rmsg.Username == "" {
rmsg.Username = unknownUser
@ -176,7 +191,7 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
if update.Message == nil && update.ChannelPost == nil &&
update.EditedMessage == nil && update.EditedChannelPost == nil {
b.Log.Error("Getting nil messages, this shouldn't happen.")
b.Log.Info("Received event without messages, skipping.")
continue
}
@ -184,6 +199,8 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
spew.Dump(update.Message)
}
b.handleGroupUpdate(update)
var message *tgbotapi.Message
rmsg := config.Message{Account: b.Account, Extra: make(map[string][]interface{})}
@ -202,9 +219,14 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
// set the ID's from the channel or group message
rmsg.ID = strconv.Itoa(message.MessageID)
rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10)
if message.IsTopicMessage {
rmsg.Channel += "/" + strconv.Itoa(message.MessageThreadID)
}
// preserve threading from telegram reply
if message.ReplyToMessage != nil {
if message.ReplyToMessage != nil &&
// Used to check if the message was a reply to the root topic
(!message.IsTopicMessage || message.ReplyToMessage.MessageID != message.MessageThreadID) {
rmsg.ParentID = strconv.Itoa(message.ReplyToMessage.MessageID)
}
@ -241,6 +263,50 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
}
}
func (b *Btelegram) handleGroupUpdate(update tgbotapi.Update) {
if msg := update.Message; msg != nil {
switch {
case msg.NewChatMembers != nil:
b.handleUserJoin(update)
case msg.LeftChatMember != nil:
b.handleUserLeave(update)
}
}
}
func (b *Btelegram) handleUserJoin(update tgbotapi.Update) {
msg := update.Message
for _, user := range msg.NewChatMembers {
rmsg := config.Message{
UserID: strconv.FormatInt(user.ID, 10),
Username: user.FirstName, // for some reason all the other name felids are empty on this event (at least for me)
Channel: strconv.FormatInt(msg.Chat.ID, 10),
Account: b.Account,
Protocol: b.Protocol,
Event: config.EventJoinLeave,
Text: "joined chat",
}
b.Remote <- rmsg
}
}
func (b *Btelegram) handleUserLeave(update tgbotapi.Update) {
msg := update.Message
user := msg.LeftChatMember
rmsg := config.Message{
UserID: strconv.FormatInt(user.ID, 10),
Username: user.FirstName, // for some reason all the other name felids are empty on this event (at least for me)
Channel: strconv.FormatInt(msg.Chat.ID, 10),
Account: b.Account,
Protocol: b.Protocol,
Event: config.EventJoinLeave,
Text: "left chat",
}
b.Remote <- rmsg
}
// handleDownloadAvatar downloads the avatar of userid from channel
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
// logs an error message if it fails
@ -317,12 +383,12 @@ func (b *Btelegram) maybeConvertWebp(name *string, data *[]byte) {
// handleDownloadFile handles file download
func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error {
size := 0
size := int64(0)
var url, name, text string
switch {
case message.Sticker != nil:
text, name, url = b.getDownloadInfo(message.Sticker.FileID, ".webp", true)
size = message.Sticker.FileSize
size = int64(message.Sticker.FileSize)
case message.Voice != nil:
text, name, url = b.getDownloadInfo(message.Voice.FileID, ".ogg", true)
size = message.Voice.FileSize
@ -339,7 +405,7 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa
text = " " + message.Document.FileName + " : " + url
case message.Photo != nil:
photos := message.Photo
size = photos[len(photos)-1].FileSize
size = int64(photos[len(photos)-1].FileSize)
text, name, url = b.getDownloadInfo(photos[len(photos)-1].FileID, "", true)
}
@ -443,7 +509,7 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error
}
// handleUploadFile handles native upload of files
func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, parentID int) (string, error) {
func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, threadid int, parentID int) (string, error) {
var media []interface{}
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
@ -451,6 +517,11 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, parentID
Name: fi.Name,
Bytes: *fi.Data,
}
if b.GetString("MessageFormat") == HTMLFormat {
fi.Comment = makeHTML(html.EscapeString(fi.Comment))
}
switch filepath.Ext(fi.Name) {
case ".jpg", ".jpe", ".png":
pc := tgbotapi.NewInputMediaPhoto(file)
@ -488,7 +559,7 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, parentID
}
}
return b.sendMediaFiles(msg, chatid, parentID, media)
return b.sendMediaFiles(msg, chatid, threadid, parentID, media)
}
func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string {

View File

@ -2,7 +2,6 @@ package btelegram
import (
"bytes"
"html"
"github.com/russross/blackfriday"
)
@ -24,10 +23,16 @@ func (options *customHTML) Paragraph(out *bytes.Buffer, text func() bool) {
func (options *customHTML) BlockCode(out *bytes.Buffer, text []byte, lang string) {
out.WriteString("<pre>")
out.WriteString(html.EscapeString(string(text)))
out.WriteString(string(text))
out.WriteString("</pre>\n")
}
func (options *customHTML) CodeSpan(out *bytes.Buffer, text []byte) {
out.WriteString("<code>")
out.WriteString(string(text))
out.WriteString("</code>")
}
func (options *customHTML) Header(out *bytes.Buffer, text func() bool, level int, id string) {
options.Paragraph(out, text)
}
@ -42,6 +47,10 @@ func (options *customHTML) BlockQuote(out *bytes.Buffer, text []byte) {
out.WriteByte('\n')
}
func (options *customHTML) LineBreak(out *bytes.Buffer) {
out.WriteByte('\n')
}
func (options *customHTML) List(out *bytes.Buffer, text func() bool, flags int) {
options.Paragraph(out, text)
}

View File

@ -10,7 +10,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
tgbotapi "github.com/matterbridge/telegram-bot-api/v6"
)
const (
@ -86,11 +86,41 @@ func TGGetParseMode(b *Btelegram, username string, text string) (textout string,
return textout, parsemode
}
func (b *Btelegram) getIds(channel string) (int64, int, error) {
var chatid int64
topicid := 0
// get the chatid
if strings.Contains(channel, "/") { //nolint:nestif
s := strings.Split(channel, "/")
if len(s) < 2 {
b.Log.Errorf("Invalid channel format: %#v\n", channel)
return 0, 0, nil
}
id, err := strconv.ParseInt(s[0], 10, 64)
if err != nil {
return 0, 0, err
}
chatid = id
tid, err := strconv.Atoi(s[1])
if err != nil {
return 0, 0, err
}
topicid = tid
} else {
id, err := strconv.ParseInt(channel, 10, 64)
if err != nil {
return 0, 0, err
}
chatid = id
}
return chatid, topicid, nil
}
func (b *Btelegram) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
// get the chatid
chatid, err := strconv.ParseInt(msg.Channel, 10, 64)
chatid, topicid, err := b.getIds(msg.Channel)
if err != nil {
return "", err
}
@ -101,7 +131,7 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
}
if b.GetString("MessageFormat") == HTMLFormat {
msg.Text = makeHTML(msg.Text)
msg.Text = makeHTML(html.EscapeString(msg.Text))
}
// Delete message
@ -123,13 +153,13 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
if _, msgErr := b.sendMessage(chatid, rmsg.Username, rmsg.Text, parentID); msgErr != nil {
if _, msgErr := b.sendMessage(chatid, topicid, rmsg.Username, rmsg.Text, parentID); msgErr != nil {
b.Log.Errorf("sendMessage failed: %s", msgErr)
}
}
// check if we have files to upload (from slack, telegram or mattermost)
if len(msg.Extra["file"]) > 0 {
return b.handleUploadFile(&msg, chatid, parentID)
return b.handleUploadFile(&msg, chatid, topicid, parentID)
}
}
@ -143,7 +173,7 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
// Ignore empty text field needs for prevent double messages from whatsapp to telegram
// when sending media with text caption
if msg.Text != "" {
return b.sendMessage(chatid, msg.Username, msg.Text, parentID)
return b.sendMessage(chatid, topicid, msg.Username, msg.Text, parentID)
}
return "", nil
@ -157,9 +187,12 @@ func (b *Btelegram) getFileDirectURL(id string) string {
return res
}
func (b *Btelegram) sendMessage(chatid int64, username, text string, parentID int) (string, error) {
func (b *Btelegram) sendMessage(chatid int64, topicid int, username, text string, parentID int) (string, error) {
m := tgbotapi.NewMessage(chatid, "")
m.Text, m.ParseMode = TGGetParseMode(b, username, text)
if topicid != 0 {
m.BaseChat.MessageThreadID = topicid
}
m.ReplyToMessageID = parentID
m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview")
@ -171,11 +204,19 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string, parentID in
}
// sendMediaFiles native upload media files via media group
func (b *Btelegram) sendMediaFiles(msg *config.Message, chatid int64, parentID int, media []interface{}) (string, error) {
func (b *Btelegram) sendMediaFiles(msg *config.Message, chatid int64, threadid int, parentID int, media []interface{}) (string, error) {
if len(media) == 0 {
return "", nil
}
mg := tgbotapi.MediaGroupConfig{ChatID: chatid, ChannelUsername: msg.Username, Media: media, ReplyToMessageID: parentID}
mg := tgbotapi.MediaGroupConfig{
BaseChat: tgbotapi.BaseChat{
ChatID: chatid,
MessageThreadID: threadid,
ChannelUsername: msg.Username,
ReplyToMessageID: parentID,
},
Media: media,
}
messages, err := b.c.SendMediaGroup(mg)
if err != nil {
return "", err

View File

@ -1,3 +1,4 @@
//go:build whatsappmulti
// +build whatsappmulti
package bwhatsapp
@ -20,9 +21,83 @@ func (b *Bwhatsapp) eventHandler(evt interface{}) {
switch e := evt.(type) {
case *events.Message:
b.handleMessage(e)
case *events.GroupInfo:
b.handleGroupInfo(e)
}
}
func (b *Bwhatsapp) handleGroupInfo(event *events.GroupInfo) {
b.Log.Debugf("Receiving event %#v", event)
switch {
case event.Join != nil:
b.handleUserJoin(event)
case event.Leave != nil:
b.handleUserLeave(event)
case event.Topic != nil:
b.handleTopicChange(event)
}
}
func (b *Bwhatsapp) handleUserJoin(event *events.GroupInfo) {
for _, joinedJid := range event.Join {
senderName := b.getSenderNameFromJID(joinedJid)
rmsg := config.Message{
UserID: joinedJid.String(),
Username: senderName,
Channel: event.JID.String(),
Account: b.Account,
Protocol: b.Protocol,
Event: config.EventJoinLeave,
Text: "joined chat",
}
b.Remote <- rmsg
}
}
func (b *Bwhatsapp) handleUserLeave(event *events.GroupInfo) {
for _, leftJid := range event.Leave {
senderName := b.getSenderNameFromJID(leftJid)
rmsg := config.Message{
UserID: leftJid.String(),
Username: senderName,
Channel: event.JID.String(),
Account: b.Account,
Protocol: b.Protocol,
Event: config.EventJoinLeave,
Text: "left chat",
}
b.Remote <- rmsg
}
}
func (b *Bwhatsapp) handleTopicChange(event *events.GroupInfo) {
msg := event.Topic
senderJid := msg.TopicSetBy
senderName := b.getSenderNameFromJID(senderJid)
text := msg.Topic
if text == "" {
text = "removed topic"
}
rmsg := config.Message{
UserID: senderJid.String(),
Username: senderName,
Channel: event.JID.String(),
Account: b.Account,
Protocol: b.Protocol,
Event: config.EventTopicChange,
Text: "Topic changed: " + text,
}
b.Remote <- rmsg
}
func (b *Bwhatsapp) handleMessage(message *events.Message) {
msg := message.Message
switch {
@ -30,7 +105,7 @@ func (b *Bwhatsapp) handleMessage(message *events.Message) {
return
}
b.Log.Infof("Receiving message %#v", msg)
b.Log.Debugf("Receiving message %#v", msg)
switch {
case msg.Conversation != nil || msg.ExtendedTextMessage != nil:
@ -43,6 +118,8 @@ func (b *Bwhatsapp) handleMessage(message *events.Message) {
b.handleDocumentMessage(message)
case msg.ImageMessage != nil:
b.handleImageMessage(message)
case msg.ProtocolMessage != nil && *msg.ProtocolMessage.Type == proto.ProtocolMessage_REVOKE:
b.handleDelete(msg.ProtocolMessage)
}
}
@ -51,10 +128,7 @@ func (b *Bwhatsapp) handleTextMessage(messageInfo types.MessageInfo, msg *proto.
senderJID := messageInfo.Sender
channel := messageInfo.Chat
senderName := b.getSenderName(messageInfo.Sender)
if senderName == "" {
senderName = "Someone" // don't expose telephone number
}
senderName := b.getSenderName(messageInfo)
if msg.GetExtendedTextMessage() == nil && msg.GetConversation() == "" {
b.Log.Debugf("message without text content? %#v", msg)
@ -66,6 +140,10 @@ func (b *Bwhatsapp) handleTextMessage(messageInfo types.MessageInfo, msg *proto.
// nolint:nestif
if msg.GetExtendedTextMessage() == nil {
text = msg.GetConversation()
} else if msg.GetExtendedTextMessage().GetContextInfo() == nil {
// Handle pure text message with a link preview
// A pure text message with a link preview acts as an extended text message but will not contain any context info
text = msg.GetExtendedTextMessage().GetText()
} else {
text = msg.GetExtendedTextMessage().GetText()
ci := msg.GetExtendedTextMessage().GetContextInfo()
@ -74,23 +152,26 @@ func (b *Bwhatsapp) handleTextMessage(messageInfo types.MessageInfo, msg *proto.
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
}
if ci.MentionedJid != nil {
if ci.MentionedJID != nil {
// handle user mentions
for _, mentionedJID := range ci.MentionedJid {
for _, mentionedJID := range ci.MentionedJID {
numberAndSuffix := strings.SplitN(mentionedJID, "@", 2)
// mentions comes as telephone numbers and we don't want to expose it to other bridges
// replace it with something more meaninful to others
mention := b.getSenderNotify(types.NewJID(numberAndSuffix[0], types.DefaultUserServer))
if mention == "" {
mention = "someone"
}
text = strings.Replace(text, "@"+numberAndSuffix[0], "@"+mention, 1)
}
}
}
parentID := ""
if msg.GetExtendedTextMessage() != nil {
ci := msg.GetExtendedTextMessage().GetContextInfo()
parentID = getParentIdFromCtx(ci)
}
rmsg := config.Message{
UserID: senderJID.String(),
Username: senderName,
@ -99,8 +180,8 @@ func (b *Bwhatsapp) handleTextMessage(messageInfo types.MessageInfo, msg *proto.
Account: b.Account,
Protocol: b.Protocol,
Extra: make(map[string][]interface{}),
// ParentID: TODO, // TODO handle thread replies // map from Info.QuotedMessageID string
ID: messageInfo.ID,
ID: getMessageIdFormat(senderJID, messageInfo.ID),
ParentID: parentID,
}
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
@ -118,7 +199,7 @@ func (b *Bwhatsapp) handleImageMessage(msg *events.Message) {
imsg := msg.Message.GetImageMessage()
senderJID := msg.Info.Sender
senderName := b.getSenderName(senderJID)
senderName := b.getSenderName(msg.Info)
ci := imsg.GetContextInfo()
if senderJID == (types.JID{}) && ci.Participant != nil {
@ -132,7 +213,8 @@ func (b *Bwhatsapp) handleImageMessage(msg *events.Message) {
Account: b.Account,
Protocol: b.Protocol,
Extra: make(map[string][]interface{}),
ID: msg.Info.ID,
ID: getMessageIdFormat(senderJID, msg.Info.ID),
ParentID: getParentIdFromCtx(ci),
}
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
@ -181,7 +263,7 @@ func (b *Bwhatsapp) handleVideoMessage(msg *events.Message) {
imsg := msg.Message.GetVideoMessage()
senderJID := msg.Info.Sender
senderName := b.getSenderName(senderJID)
senderName := b.getSenderName(msg.Info)
ci := imsg.GetContextInfo()
if senderJID == (types.JID{}) && ci.Participant != nil {
@ -195,7 +277,8 @@ func (b *Bwhatsapp) handleVideoMessage(msg *events.Message) {
Account: b.Account,
Protocol: b.Protocol,
Extra: make(map[string][]interface{}),
ID: msg.Info.ID,
ID: getMessageIdFormat(senderJID, msg.Info.ID),
ParentID: getParentIdFromCtx(ci),
}
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
@ -213,7 +296,16 @@ func (b *Bwhatsapp) handleVideoMessage(msg *events.Message) {
fileExt = append(fileExt, ".mp4")
}
filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[0])
// Prefer .mp4 extension, otherwise fallback to first index
fileExtIndex := 0
for i, n := range fileExt {
if ".mp4" == n {
fileExtIndex = i
break
}
}
filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[fileExtIndex])
b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, imsg.GetFileLength(), imsg.GetMimetype())
@ -238,13 +330,12 @@ func (b *Bwhatsapp) handleAudioMessage(msg *events.Message) {
imsg := msg.Message.GetAudioMessage()
senderJID := msg.Info.Sender
senderName := b.getSenderName(senderJID)
senderName := b.getSenderName(msg.Info)
ci := imsg.GetContextInfo()
if senderJID == (types.JID{}) && ci.Participant != nil {
senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
}
rmsg := config.Message{
UserID: senderJID.String(),
Username: senderName,
@ -252,7 +343,8 @@ func (b *Bwhatsapp) handleAudioMessage(msg *events.Message) {
Account: b.Account,
Protocol: b.Protocol,
Extra: make(map[string][]interface{}),
ID: msg.Info.ID,
ID: getMessageIdFormat(senderJID, msg.Info.ID),
ParentID: getParentIdFromCtx(ci),
}
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
@ -295,7 +387,7 @@ func (b *Bwhatsapp) handleDocumentMessage(msg *events.Message) {
imsg := msg.Message.GetDocumentMessage()
senderJID := msg.Info.Sender
senderName := b.getSenderName(senderJID)
senderName := b.getSenderName(msg.Info)
ci := imsg.GetContextInfo()
if senderJID == (types.JID{}) && ci.Participant != nil {
@ -309,7 +401,8 @@ func (b *Bwhatsapp) handleDocumentMessage(msg *events.Message) {
Account: b.Account,
Protocol: b.Protocol,
Extra: make(map[string][]interface{}),
ID: msg.Info.ID,
ID: getMessageIdFormat(senderJID, msg.Info.ID),
ParentID: getParentIdFromCtx(ci),
}
if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
@ -335,10 +428,27 @@ func (b *Bwhatsapp) handleDocumentMessage(msg *events.Message) {
}
// Move file to bridge storage
helper.HandleDownloadData(b.Log, &rmsg, filename, "document", "", &data, b.General)
helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
func (b *Bwhatsapp) handleDelete(messageInfo *proto.ProtocolMessage) {
sender, _ := types.ParseJID(*messageInfo.Key.Participant)
rmsg := config.Message{
Account: b.Account,
Protocol: b.Protocol,
ID: getMessageIdFormat(sender, *messageInfo.Key.ID),
Event: config.EventMsgDelete,
Text: config.EventMsgDelete,
Channel: *messageInfo.Key.RemoteJID,
}
b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}

View File

@ -1,3 +1,4 @@
//go:build whatsappmulti
// +build whatsappmulti
package bwhatsapp
@ -6,6 +7,10 @@ import (
"fmt"
"strings"
goproto "google.golang.org/protobuf/proto"
"go.mau.fi/whatsmeow"
"go.mau.fi/whatsmeow/binary/proto"
"go.mau.fi/whatsmeow/store"
"go.mau.fi/whatsmeow/store/sqlstore"
"go.mau.fi/whatsmeow/types"
@ -17,24 +22,7 @@ type ProfilePicInfo struct {
Status int16 `json:"status"`
}
func (b *Bwhatsapp) getSenderName(senderJid types.JID) string {
if sender, exists := b.contacts[senderJid]; exists {
if sender.FullName != "" {
return sender.FullName
}
// if user is not in phone contacts
// it is the most obvious scenario unless you sync your phone contacts with some remote updated source
// users can change it in their WhatsApp settings -> profile -> click on Avatar
if sender.PushName != "" {
return sender.PushName
}
if sender.FirstName != "" {
return sender.FirstName
}
}
// try to reload this contact
func (b *Bwhatsapp) reloadContacts() {
if _, err := b.wc.Store.Contacts.GetAllContacts(); err != nil {
b.Log.Errorf("error on update of contacts: %v", err)
}
@ -47,37 +35,91 @@ func (b *Bwhatsapp) getSenderName(senderJid types.JID) string {
if len(allcontacts) > 0 {
b.contacts = allcontacts
}
}
if sender, exists := b.contacts[senderJid]; exists {
if sender.FullName != "" {
return sender.FullName
}
// if user is not in phone contacts
// it is the most obvious scenario unless you sync your phone contacts with some remote updated source
// users can change it in their WhatsApp settings -> profile -> click on Avatar
if sender.PushName != "" {
return sender.PushName
}
func (b *Bwhatsapp) getSenderName(info types.MessageInfo) string {
// Parse AD JID
var senderJid types.JID
senderJid.User, senderJid.Server = info.Sender.User, info.Sender.Server
if sender.FirstName != "" {
return sender.FirstName
}
sender, exists := b.contacts[senderJid]
if !exists || (sender.FullName == "" && sender.FirstName == "") {
b.reloadContacts() // Contacts may need to be reloaded
sender, exists = b.contacts[senderJid]
}
if exists && sender.FullName != "" {
return sender.FullName
}
if info.PushName != "" {
return info.PushName
}
if exists && sender.FirstName != "" {
return sender.FirstName
}
return "Someone"
}
func (b *Bwhatsapp) getSenderNameFromJID(senderJid types.JID) string {
sender, exists := b.contacts[senderJid]
if !exists || (sender.FullName == "" && sender.FirstName == "") {
b.reloadContacts() // Contacts may need to be reloaded
sender, exists = b.contacts[senderJid]
}
if exists && sender.FullName != "" {
return sender.FullName
}
if exists && sender.FirstName != "" {
return sender.FirstName
}
if sender.PushName != "" {
return sender.PushName
}
return "Someone"
}
func (b *Bwhatsapp) getSenderNotify(senderJid types.JID) string {
if sender, exists := b.contacts[senderJid]; exists {
sender, exists := b.contacts[senderJid]
if !exists || (sender.FullName == "" && sender.PushName == "" && sender.FirstName == "") {
b.reloadContacts() // Contacts may need to be reloaded
sender, exists = b.contacts[senderJid]
}
if !exists {
return "someone"
}
if exists && sender.FullName != "" {
return sender.FullName
}
if exists && sender.PushName != "" {
return sender.PushName
}
return ""
if exists && sender.FirstName != "" {
return sender.FirstName
}
return "someone"
}
func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*types.ProfilePictureInfo, error) {
pjid, _ := types.ParseJID(jid)
info, err := b.wc.GetProfilePictureInfo(pjid, true)
info, err := b.wc.GetProfilePictureInfo(pjid, &whatsmeow.GetProfilePictureParams{
Preview: true,
})
if err != nil {
return nil, fmt.Errorf("failed to get avatar: %v", err)
}
@ -94,7 +136,7 @@ func isGroupJid(identifier string) bool {
func (b *Bwhatsapp) getDevice() (*store.Device, error) {
device := &store.Device{}
storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_foreign_keys=on&_pragma=busy_timeout=10000", nil)
storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_pragma=foreign_keys(1)&_pragma=busy_timeout=10000", nil)
if err != nil {
return device, fmt.Errorf("failed to connect to database: %v", err)
}
@ -106,3 +148,62 @@ func (b *Bwhatsapp) getDevice() (*store.Device, error) {
return device, nil
}
func (b *Bwhatsapp) getNewReplyContext(parentID string) (*proto.ContextInfo, error) {
replyInfo, err := b.parseMessageID(parentID)
if err != nil {
return nil, err
}
sender := fmt.Sprintf("%s@%s", replyInfo.Sender.User, replyInfo.Sender.Server)
ctx := &proto.ContextInfo{
StanzaID: &replyInfo.MessageID,
Participant: &sender,
QuotedMessage: &proto.Message{Conversation: goproto.String("")},
}
return ctx, nil
}
func (b *Bwhatsapp) parseMessageID(id string) (*Replyable, error) {
// No message ID in case action is executed on a message sent before the bridge was started
// and then the bridge cache doesn't have this message ID mapped
if id == "" {
return &Replyable{MessageID: id}, nil
}
replyInfo := strings.Split(id, "/")
if len(replyInfo) == 2 {
sender, err := types.ParseJID(replyInfo[0])
if err == nil {
return &Replyable{
MessageID: types.MessageID(replyInfo[1]),
Sender: sender,
}, nil
}
}
err := fmt.Errorf("MessageID does not match format of {senderJID}:{messageID} : \"%s\"", id)
return &Replyable{MessageID: id}, err
}
func getParentIdFromCtx(ci *proto.ContextInfo) string {
if ci != nil && ci.StanzaID != nil {
senderJid, err := types.ParseJID(*ci.Participant)
if err == nil {
return getMessageIdFormat(senderJid, *ci.StanzaID)
}
}
return ""
}
func getMessageIdFormat(jid types.JID, messageID string) string {
// we're crafting our own JID str as AD JID format messes with how stuff looks on a webclient
jidStr := fmt.Sprintf("%s@%s", jid.User, jid.Server)
return fmt.Sprintf("%s/%s", jidStr, messageID)
}

View File

@ -1,3 +1,4 @@
//go:build whatsappmulti
// +build whatsappmulti
package bwhatsapp
@ -34,11 +35,17 @@ const (
type Bwhatsapp struct {
*bridge.Config
startedAt time.Time
wc *whatsmeow.Client
contacts map[types.JID]types.ContactInfo
users map[string]types.ContactInfo
userAvatars map[string]string
startedAt time.Time
wc *whatsmeow.Client
contacts map[types.JID]types.ContactInfo
users map[string]types.ContactInfo
userAvatars map[string]string
joinedGroups []*types.GroupInfo
}
type Replyable struct {
MessageID types.MessageID
Sender types.JID
}
// New Create a new WhatsApp bridge. This will be called for each [whatsapp.<server>] entry you have in the config file
@ -120,6 +127,11 @@ func (b *Bwhatsapp) Connect() error {
return errors.New("failed to get contacts: " + err.Error())
}
b.joinedGroups, err = b.wc.GetJoinedGroups()
if err != nil {
return errors.New("failed to get list of joined groups: " + err.Error())
}
b.startedAt = time.Now()
// map all the users
@ -165,11 +177,6 @@ func (b *Bwhatsapp) Disconnect() error {
func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
byJid := isGroupJid(channel.Name)
groups, err := b.wc.GetJoinedGroups()
if err != nil {
return err
}
// verify if we are member of the given group
if byJid {
gJID, err := types.ParseJID(channel.Name)
@ -177,7 +184,7 @@ func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
return err
}
for _, group := range groups {
for _, group := range b.joinedGroups {
if group.JID == gJID {
return nil
}
@ -186,7 +193,7 @@ func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
foundGroups := []string{}
for _, group := range groups {
for _, group := range b.joinedGroups {
if group.Name == channel.Name {
foundGroups = append(foundGroups, group.Name)
}
@ -195,7 +202,7 @@ func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
switch len(foundGroups) {
case 0:
// didn't match any group - print out possibilites
for _, group := range groups {
for _, group := range b.joinedGroups {
b.Log.Infof("%s %s", group.JID, group.Name)
}
return fmt.Errorf("please specify group's JID from the list above instead of the name '%s'", channel.Name)
@ -212,6 +219,8 @@ func (b *Bwhatsapp) PostDocumentMessage(msg config.Message, filetype string) (st
fi := msg.Extra["file"][0].(config.FileInfo)
caption := msg.Username + fi.Comment
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaDocument)
if err != nil {
return "", err
@ -219,22 +228,29 @@ func (b *Bwhatsapp) PostDocumentMessage(msg config.Message, filetype string) (st
// Post document message
var message proto.Message
var ctx *proto.ContextInfo
if msg.ParentID != "" {
ctx, _ = b.getNewReplyContext(msg.ParentID)
}
message.DocumentMessage = &proto.DocumentMessage{
Title: &fi.Name,
FileName: &fi.Name,
Mimetype: &filetype,
Caption: &caption,
MediaKey: resp.MediaKey,
FileEncSha256: resp.FileEncSHA256,
FileSha256: resp.FileSHA256,
FileEncSHA256: resp.FileEncSHA256,
FileSHA256: resp.FileSHA256,
FileLength: goproto.Uint64(resp.FileLength),
Url: &resp.URL,
URL: &resp.URL,
DirectPath: &resp.DirectPath,
ContextInfo: ctx,
}
b.Log.Debugf("=> Sending %#v", msg)
b.Log.Debugf("=> Sending %#v as a document", msg)
ID := whatsmeow.GenerateMessageID()
_, err = b.wc.SendMessage(groupJID, ID, &message)
_, err = b.wc.SendMessage(context.TODO(), groupJID, &message, whatsmeow.SendRequestExtra{ID: ID})
return ID, err
}
@ -242,8 +258,6 @@ func (b *Bwhatsapp) PostDocumentMessage(msg config.Message, filetype string) (st
// Post an image message from the bridge to WhatsApp
// Handle, for sure image/jpeg, image/png and image/gif MIME types
func (b *Bwhatsapp) PostImageMessage(msg config.Message, filetype string) (string, error) {
groupJID, _ := types.ParseJID(msg.Channel)
fi := msg.Extra["file"][0].(config.FileInfo)
caption := msg.Username + fi.Comment
@ -254,21 +268,100 @@ func (b *Bwhatsapp) PostImageMessage(msg config.Message, filetype string) (strin
}
var message proto.Message
var ctx *proto.ContextInfo
if msg.ParentID != "" {
ctx, _ = b.getNewReplyContext(msg.ParentID)
}
message.ImageMessage = &proto.ImageMessage{
Mimetype: &filetype,
Caption: &caption,
MediaKey: resp.MediaKey,
FileEncSha256: resp.FileEncSHA256,
FileSha256: resp.FileSHA256,
FileEncSHA256: resp.FileEncSHA256,
FileSHA256: resp.FileSHA256,
FileLength: goproto.Uint64(resp.FileLength),
Url: &resp.URL,
URL: &resp.URL,
DirectPath: &resp.DirectPath,
ContextInfo: ctx,
}
b.Log.Debugf("=> Sending %#v", msg)
b.Log.Debugf("=> Sending %#v as an image", msg)
ID := whatsmeow.GenerateMessageID()
_, err = b.wc.SendMessage(groupJID, ID, &message)
return b.sendMessage(msg, &message)
}
// Post a video message from the bridge to WhatsApp
func (b *Bwhatsapp) PostVideoMessage(msg config.Message, filetype string) (string, error) {
fi := msg.Extra["file"][0].(config.FileInfo)
caption := msg.Username + fi.Comment
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaVideo)
if err != nil {
return "", err
}
var message proto.Message
var ctx *proto.ContextInfo
if msg.ParentID != "" {
ctx, _ = b.getNewReplyContext(msg.ParentID)
}
message.VideoMessage = &proto.VideoMessage{
Mimetype: &filetype,
Caption: &caption,
MediaKey: resp.MediaKey,
FileEncSHA256: resp.FileEncSHA256,
FileSHA256: resp.FileSHA256,
FileLength: goproto.Uint64(resp.FileLength),
URL: &resp.URL,
DirectPath: &resp.DirectPath,
ContextInfo: ctx,
}
b.Log.Debugf("=> Sending %#v as a video", msg)
return b.sendMessage(msg, &message)
}
// Post audio inline
func (b *Bwhatsapp) PostAudioMessage(msg config.Message, filetype string) (string, error) {
groupJID, _ := types.ParseJID(msg.Channel)
fi := msg.Extra["file"][0].(config.FileInfo)
resp, err := b.wc.Upload(context.Background(), *fi.Data, whatsmeow.MediaAudio)
if err != nil {
return "", err
}
var message proto.Message
var ctx *proto.ContextInfo
if msg.ParentID != "" {
ctx, _ = b.getNewReplyContext(msg.ParentID)
}
message.AudioMessage = &proto.AudioMessage{
Mimetype: &filetype,
MediaKey: resp.MediaKey,
FileEncSHA256: resp.FileEncSHA256,
FileSHA256: resp.FileSHA256,
FileLength: goproto.Uint64(resp.FileLength),
URL: &resp.URL,
DirectPath: &resp.DirectPath,
ContextInfo: ctx,
}
b.Log.Debugf("=> Sending %#v as audio", msg)
ID, err := b.sendMessage(msg, &message)
var captionMessage proto.Message
caption := msg.Username + fi.Comment + "\u2B06" // the char on the end is upwards arrow emoji
captionMessage.Conversation = &caption
captionID := whatsmeow.GenerateMessageID()
_, err = b.wc.SendMessage(context.TODO(), groupJID, &captionMessage, whatsmeow.SendRequestExtra{ID: captionID})
return ID, err
}
@ -277,6 +370,9 @@ func (b *Bwhatsapp) PostImageMessage(msg config.Message, filetype string) (strin
func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
groupJID, _ := types.ParseJID(msg.Channel)
extendedMsgID, _ := b.parseMessageID(msg.ID)
msg.ID = extendedMsgID.MessageID
b.Log.Debugf("=> Receiving %#v", msg)
// Delete message
@ -315,19 +411,46 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
switch filetype {
case "image/jpeg", "image/png", "image/gif":
return b.PostImageMessage(msg, filetype)
case "video/mp4", "video/3gpp": // TODO: Check if codecs are supported by WA
return b.PostVideoMessage(msg, filetype)
case "audio/ogg":
return b.PostAudioMessage(msg, "audio/ogg; codecs=opus") // TODO: Detect if it is actually OPUS
case "audio/aac", "audio/mp4", "audio/amr", "audio/mpeg":
return b.PostAudioMessage(msg, filetype)
default:
return b.PostDocumentMessage(msg, filetype)
}
}
var message proto.Message
text := msg.Username + msg.Text
var message proto.Message
// If we have a parent ID send an extended message
if msg.ParentID != "" {
replyContext, err := b.getNewReplyContext(msg.ParentID)
if err == nil {
message = proto.Message{
ExtendedTextMessage: &proto.ExtendedTextMessage{
Text: &text,
ContextInfo: replyContext,
},
}
return b.sendMessage(msg, &message)
}
}
message.Conversation = &text
ID := whatsmeow.GenerateMessageID()
_, err := b.wc.SendMessage(groupJID, ID, &message)
return ID, err
return b.sendMessage(msg, &message)
}
func (b *Bwhatsapp) sendMessage(rmsg config.Message, message *proto.Message) (string, error) {
groupJID, _ := types.ParseJID(rmsg.Channel)
ID := whatsmeow.GenerateMessageID()
_, err := b.wc.SendMessage(context.Background(), groupJID, message, whatsmeow.SendRequestExtra{ID: ID})
return getMessageIdFormat(*b.wc.Store.ID, ID), err
}

View File

@ -14,6 +14,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
lru "github.com/hashicorp/golang-lru"
"github.com/jpillora/backoff"
"github.com/matterbridge/go-xmpp"
"github.com/rs/xid"
@ -28,13 +29,20 @@ type Bxmpp struct {
connected bool
sync.RWMutex
StanzaIDs *lru.Cache
OriginIDs *lru.Cache
avatarAvailability map[string]bool
avatarMap map[string]string
}
func New(cfg *bridge.Config) bridge.Bridger {
stanzaIDs, _ := lru.New(5000)
originIDs, _ := lru.New(5000)
return &Bxmpp{
Config: cfg,
StanzaIDs: stanzaIDs,
OriginIDs: originIDs,
xmppMap: make(map[string]string),
avatarAvailability: make(map[string]bool),
avatarMap: make(map[string]string),
@ -124,12 +132,20 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
return "", nil
}
if msg.ParentNotFound() {
msg.ParentID = ""
}
// Post normal message.
var msgReplaceID string
msgID := xid.New().String()
if msg.ID != "" {
msgReplaceID = msg.ID
}
var replyID string
if res, ok := b.StanzaIDs.Get(msg.ParentID); ok {
replyID, _ = res.(string)
}
b.Log.Debugf("=> Sending message %#v", msg)
if _, err := b.xc.Send(xmpp.Chat{
Type: "groupchat",
@ -137,6 +153,7 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
Text: msg.Username + msg.Text,
ID: msgID,
ReplaceID: msgReplaceID,
ReplyID: replyID,
}); err != nil {
return "", err
}
@ -297,6 +314,11 @@ func (b *Bxmpp) handleXMPP() error {
if v.Type == "groupchat" {
b.Log.Debugf("== Receiving %#v", v)
if v.ID != "" && v.StanzaID != "" {
b.StanzaIDs.Add(v.ID, v.StanzaID)
b.OriginIDs.Add(v.StanzaID, v.ID)
}
// Skip invalid messages.
if b.skipMessage(v) {
continue
@ -321,6 +343,12 @@ func (b *Bxmpp) handleXMPP() error {
if v.ReplaceID != "" {
msgID = v.ReplaceID
}
var parentID string
if res, ok := b.OriginIDs.Get(v.ReplyID); ok {
parentID, _ = res.(string)
}
rmsg := config.Message{
Username: b.parseNick(v.Remote),
Text: v.Text,
@ -328,6 +356,7 @@ func (b *Bxmpp) handleXMPP() error {
Account: b.Account,
Avatar: avatar,
UserID: v.Remote,
ParentID: parentID,
ID: msgID,
Event: event,
}

View File

@ -106,30 +106,31 @@ func (b *Bzulip) getChannel(id int) string {
func (b *Bzulip) handleQueue() error {
for {
messages, err := b.q.GetEvents()
switch err {
case gzb.BackoffError:
time.Sleep(time.Second * 5)
case gzb.NoJSONError:
b.Log.Error("Response wasn't JSON, server down or restarting? sleeping 10 seconds")
time.Sleep(time.Second * 10)
case gzb.BadEventQueueError:
b.Log.Info("got a bad event queue id error, reconnecting")
b.bot.Queues = nil
for {
b.q, err = b.bot.RegisterAll()
if err != nil {
b.Log.Errorf("reconnecting failed: %s. Sleeping 10 seconds", err)
time.Sleep(time.Second * 10)
}
break
}
case gzb.HeartbeatError:
b.Log.Debug("heartbeat received.")
default:
b.Log.Debugf("receiving error: %#v", err)
time.Sleep(time.Second * 10)
}
if err != nil {
switch err {
case gzb.BackoffError:
time.Sleep(time.Second * 5)
case gzb.NoJSONError:
b.Log.Error("Response wasn't JSON, server down or restarting? sleeping 10 seconds")
time.Sleep(time.Second * 10)
case gzb.BadEventQueueError:
b.Log.Info("got a bad event queue id error, reconnecting")
b.bot.Queues = nil
for {
b.q, err = b.bot.RegisterAll()
if err != nil {
b.Log.Errorf("reconnecting failed: %s. Sleeping 10 seconds", err)
time.Sleep(time.Second * 10)
}
break
}
case gzb.HeartbeatError:
b.Log.Debug("heartbeat received.")
default:
b.Log.Debugf("receiving error: %#v", err)
time.Sleep(time.Second * 10)
}
continue
}
for _, m := range messages {

View File

@ -1,3 +1,31 @@
# v1.26.0
## New features
- irc: Allow substitution of bot's nick in RunCommands (irc) (#1890)
- matrix: Add Matrix username spoofing (#1875)
## Enhancements
- general: Update dependencies (#1951)
- mattermost: Remove mattermost 5 support (#1936)
- mumble: Implement sending of EventJoinLeave both to and from Mumble (#1915)
- whatsappmulti: Improve attachment handling (whatsapp) (#1928)
- whatsappmulti: Handle incoming document captions from whatsapp (#1935)
## Bugfix
- irc: Fix empty messages on IRC (#1897)
- telegram: Fix message html entities escaping when sending to Telegram (#1855)
- telegram/slack: Fix error messages in telegram and slack bridges (#1862)
- telegram: Fix telegram attachment comment formatting and escaping (#1920)
- telegram: Make the cgo lottie a build tag (-tag cgolottie) (#1955)
- whatsappmulti: Update dependencies and fix whatsmeow API changes (#1887)
- whatsappmulti: Fix the "Someone" nickname problem (whatsapp) (#1931)
This release couldn't exist without the following contributors:
@s3lph, @sas1024, @Glandos, @jx11r, @Lucki, @BuckarooBanzay, @ilmaisin, @Kufat
# v1.25.2
## Enhancements

View File

@ -0,0 +1,6 @@
text := import("text")
if outProtocol == "mumble" {
urlRE := text.re_compile(`(?is)((http|https):\/\/)?([a-z0-9-]+\.)?[a-z0-9-]+(\.[a-z]{2,6}){1,3}(\/[a-z0-9.,_\/~#&=;%+?-]*)?`)
msgText = urlRE.replace(msgText,`<a href="$0">$0</a>`)
}

View File

@ -1,11 +0,0 @@
// +build !nogitter
package bridgemap
import (
bgitter "github.com/42wim/matterbridge/bridge/gitter"
)
func init() {
FullMap["gitter"] = bgitter.New
}

View File

@ -18,8 +18,6 @@ var testconfig = []byte(`
server=""
[mattermost.test]
server=""
[gitter.42wim]
server=""
[discord.test]
server=""
[slack.test]
@ -33,11 +31,6 @@ server=""
account = "irc.freenode"
channel = "#wimtesting"
[[gateway.inout]]
account="gitter.42wim"
channel="42wim/testroom"
#channel="matterbridge/Lobby"
[[gateway.inout]]
account = "discord.test"
channel = "general"
@ -52,8 +45,6 @@ var testconfig2 = []byte(`
server=""
[mattermost.test]
server=""
[gitter.42wim]
server=""
[discord.test]
server=""
[slack.test]
@ -67,10 +58,6 @@ server=""
account = "irc.freenode"
channel = "#wimtesting"
[[gateway.in]]
account="gitter.42wim"
channel="42wim/testroom"
[[gateway.inout]]
account = "discord.test"
channel = "general"
@ -86,10 +73,6 @@ server=""
account = "irc.freenode"
channel = "#wimtesting2"
[[gateway.out]]
account="gitter.42wim"
channel="42wim/testroom"
[[gateway.out]]
account = "discord.test"
channel = "general2"
@ -184,31 +167,18 @@ func maketestRouter(input []byte) *Router {
}
return r
}
func TestNewRouter(t *testing.T) {
r := maketestRouter(testconfig)
assert.Equal(t, 1, len(r.Gateways))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels))
assert.Equal(t, 3, len(r.Gateways["bridge1"].Bridges))
assert.Equal(t, 3, len(r.Gateways["bridge1"].Channels))
r = maketestRouter(testconfig2)
assert.Equal(t, 2, len(r.Gateways))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))
assert.Equal(t, 3, len(r.Gateways["bridge2"].Bridges))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels))
assert.Equal(t, 3, len(r.Gateways["bridge2"].Channels))
assert.Equal(t, &config.ChannelInfo{
Name: "42wim/testroom",
Direction: "out",
ID: "42wim/testroomgitter.42wim",
Account: "gitter.42wim",
SameChannel: map[string]bool{"bridge2": false},
}, r.Gateways["bridge2"].Channels["42wim/testroomgitter.42wim"])
assert.Equal(t, &config.ChannelInfo{
Name: "42wim/testroom",
Direction: "in",
ID: "42wim/testroomgitter.42wim",
Account: "gitter.42wim",
SameChannel: map[string]bool{"bridge1": false},
}, r.Gateways["bridge1"].Channels["42wim/testroomgitter.42wim"])
assert.Equal(t, 3, len(r.Gateways["bridge1"].Bridges))
assert.Equal(t, 2, len(r.Gateways["bridge2"].Bridges))
assert.Equal(t, 3, len(r.Gateways["bridge1"].Channels))
assert.Equal(t, 2, len(r.Gateways["bridge2"].Channels))
assert.Equal(t, &config.ChannelInfo{
Name: "general",
Direction: "inout",
@ -241,8 +211,6 @@ func TestGetDestChannel(t *testing.T) {
SameChannel: map[string]bool{"bridge1": false},
Options: config.ChannelOptions{Key: ""},
}}, r.Gateways["bridge1"].getDestChannel(msg, *br))
case "gitter.42wim":
assert.Equal(t, []config.ChannelInfo(nil), r.Gateways["bridge1"].getDestChannel(msg, *br))
case "irc.freenode":
assert.Equal(t, []config.ChannelInfo(nil), r.Gateways["bridge1"].getDestChannel(msg, *br))
}
@ -420,6 +388,7 @@ func (s *ignoreTestSuite) SetupSuite() {
logger.SetOutput(ioutil.Discard)
s.gw = &Gateway{logger: logrus.NewEntry(logger)}
}
func (s *ignoreTestSuite) TestIgnoreTextEmpty() {
extraFile := make(map[string][]interface{})
extraAttach := make(map[string][]interface{})
@ -461,7 +430,6 @@ func (s *ignoreTestSuite) TestIgnoreTextEmpty() {
output := s.gw.ignoreTextEmpty(testcase.input)
s.Assert().Equalf(testcase.output, output, "case '%s' failed", testname)
}
}
func (s *ignoreTestSuite) TestIgnoreTexts() {

196
go.mod
View File

@ -1,163 +1,153 @@
module github.com/42wim/matterbridge
require (
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
github.com/SevereCloud/vksdk/v2 v2.14.1
github.com/bwmarrin/discordgo v0.25.0
github.com/d5/tengo/v2 v2.12.0
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.5.4
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/gomarkdown/markdown v0.0.0-20220607163217-45f7c050e2d1
github.com/google/gops v0.3.23
github.com/gorilla/schema v1.2.0
github.com/gorilla/websocket v1.5.0
github.com/SevereCloud/vksdk/v2 v2.17.0
github.com/bwmarrin/discordgo v0.28.1
github.com/d5/tengo/v2 v2.17.0
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/fsnotify/fsnotify v1.7.0
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
github.com/google/gops v0.3.27
github.com/gorilla/schema v1.4.1
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
github.com/hashicorp/golang-lru v0.5.4
github.com/hashicorp/golang-lru v1.0.2
github.com/jpillora/backoff v1.0.0
github.com/keybase/go-keybase-chat-bot v0.0.0-20220322223021-75d497527469
github.com/kyokomi/emoji/v2 v2.2.9
github.com/labstack/echo/v4 v4.7.2
github.com/lrstanley/girc v0.0.0-20220507183218-96757fe3d2a2
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
github.com/kyokomi/emoji/v2 v2.2.13
github.com/labstack/echo/v4 v4.12.0
github.com/lrstanley/girc v0.0.0-20240823210506-80555f2adb03
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
github.com/matterbridge/go-xmpp v0.0.0-20240523230155-7154bfeb76e8
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
github.com/matterbridge/matterclient v0.0.0-20220430213656-07aca2731bc9
github.com/mattermost/mattermost-server/v5 v5.39.3
github.com/mattermost/mattermost-server/v6 v6.7.0
github.com/matterbridge/matterclient v0.0.0-20240817214420-3d4c3aef3dc1
github.com/matterbridge/telegram-bot-api/v6 v6.5.0
github.com/mattermost/mattermost/server/public v0.1.6
github.com/mattn/godown v0.0.1
github.com/mdp/qrterminal v1.0.1
github.com/mitchellh/mapstructure v1.5.0
github.com/nelsonken/gomf v0.0.0-20190423072027-c65cc0469e94
github.com/olahol/melody v1.2.1
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
github.com/rs/xid v1.4.0
github.com/rs/xid v1.5.0
github.com/russross/blackfriday v1.6.0
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/shazow/ssh-chat v1.10.1
github.com/sirupsen/logrus v1.8.1
github.com/slack-go/slack v0.11.0
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.7.2
github.com/sirupsen/logrus v1.9.3
github.com/slack-go/slack v0.14.0
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/vincent-petithory/dataurl v1.0.0
github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/yaegashi/msgraph.go v0.1.4
github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289
go.mau.fi/whatsmeow v0.0.0-20220624184947-57a69a641154
golang.org/x/image v0.0.0-20220617043117-41969df76e82
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
golang.org/x/text v0.3.7
go.mau.fi/whatsmeow v0.0.0-20240821142752-3d63c6fcc1a7
golang.org/x/image v0.19.0
golang.org/x/oauth2 v0.22.0
golang.org/x/text v0.17.0
gomod.garykim.dev/nc-talk v0.3.0
google.golang.org/protobuf v1.28.0
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
modernc.org/sqlite v1.17.3
google.golang.org/protobuf v1.34.2
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14
modernc.org/sqlite v1.32.0
)
require (
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Benau/go_rlottie v0.0.0-20210807002906-98c1b2421989 // indirect
github.com/Jeffail/gabs v1.4.0 // indirect
github.com/apex/log v1.9.0 // indirect
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gopackage/ddp v0.0.3 // indirect
github.com/graph-gophers/graphql-go v1.3.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.6.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
github.com/klauspost/compress v1.15.6 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
github.com/mattermost/logr v1.0.13 // indirect
github.com/mattermost/logr/v2 v2.0.15 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 // indirect
github.com/mattermost/logr/v2 v2.0.21 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.24 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/monaco-io/request v1.0.5 // indirect
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d // indirect
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/philhofer/fwd v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rickb777/date v1.12.4 // indirect
github.com/rickb777/plural v1.2.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/tinylib/msgp v1.1.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tinylib/msgp v1.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/wiggin77/cfg v1.0.2 // indirect
github.com/wiggin77/merror v1.0.3 // indirect
github.com/wiggin77/merror v1.0.5 // indirect
github.com/wiggin77/srslog v1.0.1 // indirect
go.mau.fi/libsignal v0.0.0-20220425070825-c40c839ee6a0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
go.mau.fi/libsignal v0.1.1 // indirect
go.mau.fi/util v0.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect
google.golang.org/grpc v1.65.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.36.0 // indirect
modernc.org/ccgo/v3 v3.16.6 // indirect
modernc.org/libc v1.16.7 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.1.1 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
rsc.io/qr v0.2.0 // indirect
)
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
go 1.17
go 1.22.0

2612
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -65,7 +65,7 @@ func main() {
if err = r.Start(); err != nil {
logger.Fatalf("Starting gateway failed: %s", err)
}
logger.Printf("Gateway(s) started succesfully. Now relaying messages")
logger.Printf("Gateway(s) started successfully. Now relaying messages")
select {}
}

61
matterbridge.toml.multi Normal file
View File

@ -0,0 +1,61 @@
#WARNING: as this file contains credentials, be sure to set correct file permissions
[irc]
[irc.foo]
Server="irc.myfooserver.com:6667"
Nick="matterbot"
# Can also connect to multiple different servers of the same protocol:
[irc]
[irc.bar]
Server="irc.mybarserver.com:6667"
Nick="matterbot"
[telegram]
[telegram.mytelegram]
Token="123456789:FillInYourTokenHereThatIsImportant"
[mattermost]
[mattermost.work]
#do not prefix it wit http:// or https://
Server="yourmattermostserver.domain"
Team="yourteam"
Login="yourlogin"
Password="yourpass"
PrefixMessagesWithNick=true
# Bridge 1: Copy all messages from all rooms to all rooms.
# This shows how you can have multiple rooms in a single bridge.
[[gateway]]
name="cats-are-cool"
enable=true
[[gateway.inout]]
account="irc.foo"
channel="#cats-are-cool"
[[gateway.inout]]
account="irc.bar"
channel="#cats-are-cool"
[[gateway.inout]]
account="telegram.mytelegram"
channel="-1234567890123"
[[gateway.inout]]
account="mattermost.work"
channel="cats-are-cool"
# Bridge 2: Copy some messages from some rooms to some rooms.
# This shows how you can have multiple bridges.
[[gateway]]
name="dog-announcements"
enable=true
[[gateway.in]]
account="irc.foo"
channel="#dog-announcements"
[[gateway.in]]
account="irc.bar"
channel="#dog-announcements"
[[gateway.out]]
account="telegram.mytelegram"
channel="-9876543219876"
[[gateway.out]]
account="mattermost.work"
channel="dog-announcements"

View File

@ -122,10 +122,11 @@ RejoinDelay=0
#Only works in IRC right now.
ColorNicks=false
#RunCommands allows you to send RAW irc commands after connection
#RunCommands allows you to send RAW irc commands after connection.
#The string {BOTNICK} (case sensitive) will be replaced with the bot's current nickname.
#Array of strings
#OPTIONAL (default empty)
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"]
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something", "MODE {BOTNICK} +B"]
#PingDelay specifies how long to wait to send a ping to the irc server.
#You can use s for second, m for minute
@ -162,7 +163,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -187,7 +188,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -197,7 +198,7 @@ ShowJoinPart=false
VerboseJoinPart=false
#Do not send joins/parts to other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
NoSendJoinPart=false
@ -300,7 +301,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#Nicks you want to replace.
#See ReplaceMessages for syntaxA
#See ReplaceMessages for syntax
#OPTIONAL (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -324,7 +325,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -467,7 +468,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -491,12 +492,12 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
#Do not send joins/parts to other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
NoSendJoinPart=false
@ -512,86 +513,9 @@ ShowTopicChange=false
###################################################################
#Gitter section
#Best to make a dedicated gitter account for the bot.
#Gitter has been moved to matrix - see https://github.com/42wim/matterbridge/issues/1969 how to migrate
###################################################################
[gitter]
#You can configure multiple servers "[gitter.name]" or "[gitter.name2]"
#In this example we use [gitter.myproject]
#REQUIRED
[gitter.myproject]
#Token to connect with Gitter API
#You can get your token by going to https://developer.gitter.im/docs/welcome and SIGN IN
#REQUIRED
Token="Yourtokenhere"
## RELOADABLE SETTINGS
## Settings below can be reloaded by editing the file
#Nicks you want to ignore.
#Regular expressions supported
#Messages from those users will not be sent to other bridges.
#OPTIONAL
IgnoreNicks="ircspammer1 ircspammer2"
#Messages you want to ignore.
#Messages matching these regexp will be ignored and not sent to other bridges
#See https://regex-golang.appspot.com/assets/html/index.html for more regex info
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
IgnoreMessages="^~~ badword"
#messages you want to replace.
#it replaces outgoing messages from the bridge.
#so you need to place it by the sending bridge definition.
#regular expressions supported
#some examples:
#this replaces cat => dog and sleep => awake
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
#this replaces every number with number. 123 => numbernumbernumber
#replacemessages=[ ["[0-9]","number"] ]
#optional (default empty)
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
#Extractnicks is used to for example rewrite messages from other relaybots
#See https://github.com/42wim/matterbridge/issues/713 and https://github.com/42wim/matterbridge/issues/466
#some examples:
#this replaces a message like "Relaybot: <relayeduser> something interesting" to "relayeduser: something interesting"
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ]
#you can use multiple entries for multiplebots
#this also replaces a message like "otherbot: (relayeduser) something else" to "relayeduser: something else"
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ]
#OPTIONAL (default empty)
ExtractNicks=[ ["otherbot","<(.*?)>\\s+" ] ]
#extra label that can be used in the RemoteNickFormat
#optional (default empty)
Label=""
#RemoteNickFormat defines how remote users appear on this bridge
#See [general] config section for default options
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
#It will strip other characters from the nick
#OPTIONAL (default false)
StripNick=false
#Enable to show topic changes from other bridges
#Only works hiding/show topic changes from slack bridge for now
#OPTIONAL (default false)
ShowTopicChange=false
###################################################################
#
# Keybase
@ -662,7 +586,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -686,7 +610,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -801,7 +725,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -825,12 +749,12 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
#Do not send joins/parts to other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
NoSendJoinPart=false
@ -1001,10 +925,17 @@ ShowTopicChange=false
# Supported from the following bridges: slack
SyncTopic=false
#Message to show when a message is too big
#Default "<clipped message>"
# Message to show when a message is too big
# Default "<clipped message>"
MessageClipped="<clipped message>"
# Before clipping, try to split messages into at most this many parts. 0 is treated like 1.
# Be careful with large numbers, as this might cause flooding.
# Example: A maximum telegram message of 4096 bytes is received. This requires 3 Discord
# messages (each capped at a hardcoded 1950 bytes).
# Default 1
MessageSplitMaxCount=3
###################################################################
#telegram section
###################################################################
@ -1109,7 +1040,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -1137,7 +1068,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -1250,7 +1181,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -1274,7 +1205,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -1362,7 +1293,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -1386,10 +1317,15 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
#Rename the bot in the current room to the username of the message
#This will make an additional API request per message and will probably count towards rate limits
#OPTIONAL (default false)
SpoofUsername=false
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
#It will strip other characters from the nick
#OPTIONAL (default false)
@ -1454,7 +1390,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -1478,7 +1414,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -1566,6 +1502,15 @@ SkipTLSVerify=false
#Default "<clipped message>"
MessageClipped="<clipped message>"
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
#Do not send joins/parts to other bridges
#OPTIONAL (default false)
NoSendJoinPart=false
###################################################################
#VK
###################################################################
@ -1654,7 +1599,7 @@ IgnoreMessages="^~~ badword"
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#see replacemessages for syntax
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
@ -1678,7 +1623,7 @@ Label=""
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#Currently works for messages from the following bridges: irc, mattermost, mumble, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
@ -1941,6 +1886,7 @@ enable=true
# nctalk | token | xs25tz5y | The token in the URL when you are in a chat. It will be the last part of the URL.
# -------------------------------------------------------------------------------------------------------------------------------------
# telegram | chatid | -123456789 | A large negative number. see https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
# | chatid/topicid | -123456789/12 | A large negative number/number.
# -------------------------------------------------------------------------------------------------------------------------------------
# vk | peerid | 2000000002 | A number that starts form 2000000000. Use --debug and send any message in chat to get PeerID in the logs
# -------------------------------------------------------------------------------------------------------------------------------------

View File

@ -1,7 +1,7 @@
#WARNING: as this file contains credentials, be sure to set correct file permissions
[irc]
[irc.freenode]
Server="irc.freenode.net:6667"
[irc.libera]
Server="irc.libera.chat:6667"
Nick="matterbot"
[mattermost]
@ -17,7 +17,7 @@
name="gateway1"
enable=true
[[gateway.inout]]
account="irc.freenode"
account="irc.libera"
channel="#testing"
[[gateway.inout]]
@ -29,6 +29,6 @@ enable=true
#name="gateway2"
#enable=true
#inout = [
# { account="irc.freenode", channel="#testing", options={key="channelkey"}},
# { account="irc.libera", channel="#testing", options={key="channelkey"}},
# { account="mattermost.work", channel="off-topic" },
#]

1
matterclient/README.md Normal file
View File

@ -0,0 +1 @@
Find matterclient on https://github.com/matterbridge/matterclient

View File

@ -1,226 +0,0 @@
package matterclient
import (
"errors"
"strings"
"github.com/mattermost/mattermost-server/v5/model"
)
// GetChannels returns all channels we're members off
func (m *MMClient) GetChannels() []*model.Channel {
m.RLock()
defer m.RUnlock()
var channels []*model.Channel
// our primary team channels first
channels = append(channels, m.Team.Channels...)
for _, t := range m.OtherTeams {
if t.Id != m.Team.Id {
channels = append(channels, t.Channels...)
}
}
return channels
}
func (m *MMClient) GetChannelHeader(channelId string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
for _, t := range m.OtherTeams {
for _, channel := range append(t.Channels, t.MoreChannels...) {
if channel.Id == channelId {
return channel.Header
}
}
}
return ""
}
func getNormalisedName(channel *model.Channel) string {
if channel.Type == model.CHANNEL_GROUP {
// (deprecated in favor of ReplaceAll in go 1.12)
res := strings.Replace(channel.DisplayName, ", ", "-", -1) //nolint: gocritic
res = strings.Replace(res, " ", "_", -1) //nolint: gocritic
return res
}
return channel.Name
}
func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
if teamId != "" {
return m.getChannelIdTeam(name, teamId)
}
for _, t := range m.OtherTeams {
for _, channel := range append(t.Channels, t.MoreChannels...) {
if getNormalisedName(channel) == name {
return channel.Id
}
}
}
return ""
}
func (m *MMClient) getChannelIdTeam(name string, teamId string) string { //nolint:golint
for _, t := range m.OtherTeams {
if t.Id == teamId {
for _, channel := range append(t.Channels, t.MoreChannels...) {
if getNormalisedName(channel) == name {
return channel.Id
}
}
}
}
return ""
}
func (m *MMClient) GetChannelName(channelId string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
for _, t := range m.OtherTeams {
if t == nil {
continue
}
for _, channel := range append(t.Channels, t.MoreChannels...) {
if channel.Id == channelId {
return getNormalisedName(channel)
}
}
}
return ""
}
func (m *MMClient) GetChannelTeamId(id string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
for _, t := range append(m.OtherTeams, m.Team) {
for _, channel := range append(t.Channels, t.MoreChannels...) {
if channel.Id == id {
return channel.TeamId
}
}
}
return ""
}
func (m *MMClient) GetLastViewedAt(channelId string) int64 { //nolint:golint
m.RLock()
defer m.RUnlock()
res, resp := m.Client.GetChannelMember(channelId, m.User.Id, "")
if resp.Error != nil {
return model.GetMillis()
}
return res.LastViewedAt
}
// GetMoreChannels returns existing channels where we're not a member off.
func (m *MMClient) GetMoreChannels() []*model.Channel {
m.RLock()
defer m.RUnlock()
var channels []*model.Channel
for _, t := range m.OtherTeams {
channels = append(channels, t.MoreChannels...)
}
return channels
}
// GetTeamFromChannel returns teamId belonging to channel (DM channels have no teamId).
func (m *MMClient) GetTeamFromChannel(channelId string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
var channels []*model.Channel
for _, t := range m.OtherTeams {
channels = append(channels, t.Channels...)
if t.MoreChannels != nil {
channels = append(channels, t.MoreChannels...)
}
for _, c := range channels {
if c.Id == channelId {
if c.Type == model.CHANNEL_GROUP {
return "G"
}
return t.Id
}
}
channels = nil
}
return ""
}
func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint
m.RLock()
defer m.RUnlock()
for _, c := range m.Team.Channels {
if c.Id == channelId {
m.logger.Debug("Not joining ", channelId, " already joined.")
return nil
}
}
m.logger.Debug("Joining ", channelId)
_, resp := m.Client.AddChannelMember(channelId, m.User.Id)
if resp.Error != nil {
return resp.Error
}
return nil
}
func (m *MMClient) UpdateChannelsTeam(teamID string) error {
mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, false, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
for idx, t := range m.OtherTeams {
if t.Id == teamID {
m.Lock()
m.OtherTeams[idx].Channels = mmchannels
m.Unlock()
}
}
mmchannels, resp = m.Client.GetPublicChannelsForTeam(teamID, 0, 5000, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
for idx, t := range m.OtherTeams {
if t.Id == teamID {
m.Lock()
m.OtherTeams[idx].MoreChannels = mmchannels
m.Unlock()
}
}
return nil
}
func (m *MMClient) UpdateChannels() error {
if err := m.UpdateChannelsTeam(m.Team.Id); err != nil {
return err
}
for _, t := range m.OtherTeams {
if err := m.UpdateChannelsTeam(t.Id); err != nil {
return err
}
}
return nil
}
func (m *MMClient) UpdateChannelHeader(channelId string, header string) { //nolint:golint
channel := &model.Channel{Id: channelId, Header: header}
m.logger.Debugf("updating channelheader %#v, %#v", channelId, header)
_, resp := m.Client.UpdateChannel(channel)
if resp.Error != nil {
m.logger.Error(resp.Error)
}
}
func (m *MMClient) UpdateLastViewed(channelId string) error { //nolint:golint
m.logger.Debugf("posting lastview %#v", channelId)
view := &model.ChannelView{ChannelId: channelId}
_, resp := m.Client.ViewChannel(m.User.Id, view)
if resp.Error != nil {
m.logger.Errorf("ChannelView update for %s failed: %s", channelId, resp.Error)
return resp.Error
}
return nil
}

View File

@ -1,297 +0,0 @@
package matterclient
import (
"crypto/md5" //nolint:gosec
"crypto/tls"
"errors"
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/jpillora/backoff"
"github.com/mattermost/mattermost-server/v5/model"
)
func (m *MMClient) doLogin(firstConnection bool, b *backoff.Backoff) error {
var resp *model.Response
var appErr *model.AppError
var logmsg = "trying login"
var err error
for {
m.logger.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
if m.Credentials.Token != "" {
resp, err = m.doLoginToken()
if err != nil {
return err
}
} else {
m.User, resp = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
}
appErr = resp.Error
if appErr != nil {
d := b.Duration()
m.logger.Debug(appErr.DetailedError)
if firstConnection {
if appErr.Message == "" {
return errors.New(appErr.DetailedError)
}
return errors.New(appErr.Message)
}
m.logger.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
time.Sleep(d)
logmsg = "retrying login"
continue
}
break
}
// reset timer
b.Reset()
return nil
}
func (m *MMClient) doLoginToken() (*model.Response, error) {
var resp *model.Response
var logmsg = "trying login"
m.Client.AuthType = model.HEADER_BEARER
m.Client.AuthToken = m.Credentials.Token
if m.Credentials.CookieToken {
m.logger.Debugf(logmsg + " with cookie (MMAUTH) token")
m.Client.HttpClient.Jar = m.createCookieJar(m.Credentials.Token)
} else {
m.logger.Debugf(logmsg + " with personal token")
}
m.User, resp = m.Client.GetMe("")
if resp.Error != nil {
return resp, resp.Error
}
if m.User == nil {
m.logger.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
return resp, errors.New("invalid token")
}
return resp, nil
}
func (m *MMClient) handleLoginToken() error {
switch {
case strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN):
token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=")
if len(token) != 2 {
return errors.New("incorrect MMAUTHTOKEN. valid input is MMAUTHTOKEN=yourtoken")
}
m.Credentials.Token = token[1]
m.Credentials.CookieToken = true
case strings.Contains(m.Credentials.Pass, "token="):
token := strings.Split(m.Credentials.Pass, "token=")
if len(token) != 2 {
return errors.New("incorrect personal token. valid input is token=yourtoken")
}
m.Credentials.Token = token[1]
}
return nil
}
func (m *MMClient) initClient(firstConnection bool, b *backoff.Backoff) error {
uriScheme := "https://"
if m.NoTLS {
uriScheme = "http://"
}
// login to mattermost
m.Client = model.NewAPIv4Client(uriScheme + m.Credentials.Server)
m.Client.HttpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}, //nolint:gosec
Proxy: http.ProxyFromEnvironment,
}
m.Client.HttpClient.Timeout = time.Second * 10
// handle MMAUTHTOKEN and personal token
if err := m.handleLoginToken(); err != nil {
return err
}
// check if server alive, retry until
if err := m.serverAlive(firstConnection, b); err != nil {
return err
}
return nil
}
// initialize user and teams
func (m *MMClient) initUser() error {
m.Lock()
defer m.Unlock()
// we only load all team data on initial login.
// all other updates are for channels from our (primary) team only.
//m.logger.Debug("initUser(): loading all team data")
teams, resp := m.Client.GetTeamsForUser(m.User.Id, "")
if resp.Error != nil {
return resp.Error
}
for _, team := range teams {
idx := 0
max := 200
usermap := make(map[string]*model.User)
mmusers, resp := m.Client.GetUsersInTeam(team.Id, idx, max, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
for len(mmusers) > 0 {
for _, user := range mmusers {
usermap[user.Id] = user
}
mmusers, resp = m.Client.GetUsersInTeam(team.Id, idx, max, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
idx++
time.Sleep(time.Millisecond * 200)
}
m.logger.Infof("found %d users in team %s", len(usermap), team.Name)
t := &Team{Team: team, Users: usermap, Id: team.Id}
mmchannels, resp := m.Client.GetChannelsForTeamForUser(team.Id, m.User.Id, false, "")
if resp.Error != nil {
return resp.Error
}
t.Channels = mmchannels
mmchannels, resp = m.Client.GetPublicChannelsForTeam(team.Id, 0, 5000, "")
if resp.Error != nil {
return resp.Error
}
t.MoreChannels = mmchannels
m.OtherTeams = append(m.OtherTeams, t)
if team.Name == m.Credentials.Team {
m.Team = t
m.logger.Debugf("initUser(): found our team %s (id: %s)", team.Name, team.Id)
}
// add all users
for k, v := range t.Users {
m.Users[k] = v
}
}
return nil
}
func (m *MMClient) serverAlive(firstConnection bool, b *backoff.Backoff) error {
defer b.Reset()
for {
d := b.Duration()
// bogus call to get the serverversion
_, resp := m.Client.Logout()
if resp.Error != nil {
return fmt.Errorf("%#v", resp.Error.Error())
}
if firstConnection && !m.SkipVersionCheck && !supportedVersion(resp.ServerVersion) {
return fmt.Errorf("unsupported mattermost version: %s", resp.ServerVersion)
}
if !m.SkipVersionCheck {
m.ServerVersion = resp.ServerVersion
if m.ServerVersion == "" {
m.logger.Debugf("Server not up yet, reconnecting in %s", d)
time.Sleep(d)
} else {
m.logger.Infof("Found version %s", m.ServerVersion)
return nil
}
} else {
return nil
}
}
}
func (m *MMClient) wsConnect() {
b := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
m.WsConnected = false
wsScheme := "wss://"
if m.NoTLS {
wsScheme = "ws://"
}
// setup websocket connection
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX_V4 + "/websocket"
header := http.Header{}
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
m.logger.Debugf("WsClient: making connection: %s", wsurl)
for {
wsDialer := &websocket.Dialer{
TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}, //nolint:gosec
Proxy: http.ProxyFromEnvironment,
}
var err error
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
if err != nil {
d := b.Duration()
m.logger.Debugf("WSS: %s, reconnecting in %s", err, d)
time.Sleep(d)
continue
}
break
}
m.logger.Debug("WsClient: connected")
m.WsSequence = 1
m.WsPingChan = make(chan *model.WebSocketResponse)
// only start to parse WS messages when login is completely done
m.WsConnected = true
}
func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
var cookies []*http.Cookie
jar, _ := cookiejar.New(nil)
firstCookie := &http.Cookie{
Name: "MMAUTHTOKEN",
Value: token,
Path: "/",
Domain: m.Credentials.Server,
}
cookies = append(cookies, firstCookie)
cookieURL, _ := url.Parse("https://" + m.Credentials.Server)
jar.SetCookies(cookieURL, cookies)
return jar
}
func (m *MMClient) checkAlive() error {
// check if session still is valid
_, resp := m.Client.GetMe("")
if resp.Error != nil {
return resp.Error
}
m.logger.Debug("WS PING")
return m.sendWSRequest("ping", nil)
}
func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) error {
req := &model.WebSocketRequest{}
req.Seq = m.WsSequence
req.Action = action
req.Data = data
m.WsSequence++
m.logger.Debugf("sendWsRequest %#v", req)
return m.WsClient.WriteJSON(req)
}
func supportedVersion(version string) bool {
if strings.HasPrefix(version, "3.8.0") ||
strings.HasPrefix(version, "3.9.0") ||
strings.HasPrefix(version, "3.10.0") ||
strings.HasPrefix(version, "4.") ||
strings.HasPrefix(version, "5.") {
return true
}
return false
}
func digestString(s string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(s))) //nolint:gosec
}

View File

@ -1,294 +0,0 @@
package matterclient
import (
"encoding/json"
"fmt"
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
lru "github.com/hashicorp/golang-lru"
"github.com/jpillora/backoff"
prefixed "github.com/matterbridge/logrus-prefixed-formatter"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/sirupsen/logrus"
)
type Credentials struct {
Login string
Team string
Pass string
Token string
CookieToken bool
Server string
NoTLS bool
SkipTLSVerify bool
SkipVersionCheck bool
}
type Message struct {
Raw *model.WebSocketEvent
Post *model.Post
Team string
Channel string
Username string
Text string
Type string
UserID string
}
//nolint:golint
type Team struct {
Team *model.Team
Id string
Channels []*model.Channel
MoreChannels []*model.Channel
Users map[string]*model.User
}
type MMClient struct {
sync.RWMutex
*Credentials
Team *Team
OtherTeams []*Team
Client *model.Client4
User *model.User
Users map[string]*model.User
MessageChan chan *Message
WsClient *websocket.Conn
WsQuit bool
WsAway bool
WsConnected bool
WsSequence int64
WsPingChan chan *model.WebSocketResponse
ServerVersion string
OnWsConnect func()
logger *logrus.Entry
rootLogger *logrus.Logger
lruCache *lru.Cache
allevents bool
}
// New will instantiate a new Matterclient with the specified login details without connecting.
func New(login string, pass string, team string, server string) *MMClient {
rootLogger := logrus.New()
rootLogger.SetFormatter(&prefixed.TextFormatter{
PrefixPadding: 13,
DisableColors: true,
})
cred := &Credentials{
Login: login,
Pass: pass,
Team: team,
Server: server,
}
cache, _ := lru.New(500)
return &MMClient{
Credentials: cred,
MessageChan: make(chan *Message, 100),
Users: make(map[string]*model.User),
rootLogger: rootLogger,
lruCache: cache,
logger: rootLogger.WithFields(logrus.Fields{"prefix": "matterclient"}),
}
}
// SetDebugLog activates debugging logging on all Matterclient log output.
func (m *MMClient) SetDebugLog() {
m.rootLogger.SetFormatter(&prefixed.TextFormatter{
PrefixPadding: 13,
DisableColors: true,
FullTimestamp: false,
ForceFormatting: true,
})
}
// SetLogLevel tries to parse the specified level and if successful sets
// the log level accordingly. Accepted levels are: 'debug', 'info', 'warn',
// 'error', 'fatal' and 'panic'.
func (m *MMClient) SetLogLevel(level string) {
l, err := logrus.ParseLevel(level)
if err != nil {
m.logger.Warnf("Failed to parse specified log-level '%s': %#v", level, err)
} else {
m.rootLogger.SetLevel(l)
}
}
func (m *MMClient) EnableAllEvents() {
m.allevents = true
}
// Login tries to connect the client with the loging details with which it was initialized.
func (m *MMClient) Login() error {
// check if this is a first connect or a reconnection
firstConnection := true
if m.WsConnected {
firstConnection = false
}
m.WsConnected = false
if m.WsQuit {
return nil
}
b := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
// do initialization setup
if err := m.initClient(firstConnection, b); err != nil {
return err
}
if err := m.doLogin(firstConnection, b); err != nil {
return err
}
if err := m.initUser(); err != nil {
return err
}
if m.Team == nil {
validTeamNames := make([]string, len(m.OtherTeams))
for i, t := range m.OtherTeams {
validTeamNames[i] = t.Team.Name
}
return fmt.Errorf("Team '%s' not found in %v", m.Credentials.Team, validTeamNames)
}
m.wsConnect()
return nil
}
// Logout disconnects the client from the chat server.
func (m *MMClient) Logout() error {
m.logger.Debugf("logout as %s (team: %s) on %s", m.Credentials.Login, m.Credentials.Team, m.Credentials.Server)
m.WsQuit = true
m.WsClient.Close()
m.WsClient.UnderlyingConn().Close()
if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
m.logger.Debug("Not invalidating session in logout, credential is a token")
return nil
}
_, resp := m.Client.Logout()
if resp.Error != nil {
return resp.Error
}
return nil
}
// WsReceiver implements the core loop that manages the connection to the chat server. In
// case of a disconnect it will try to reconnect. A call to this method is blocking until
// the 'WsQuite' field of the MMClient object is set to 'true'.
func (m *MMClient) WsReceiver() {
for {
var rawMsg json.RawMessage
var err error
if m.WsQuit {
m.logger.Debug("exiting WsReceiver")
return
}
if !m.WsConnected {
time.Sleep(time.Millisecond * 100)
continue
}
if _, rawMsg, err = m.WsClient.ReadMessage(); err != nil {
m.logger.Error("error:", err)
// reconnect
m.wsConnect()
}
var event model.WebSocketEvent
if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() {
m.logger.Debugf("WsReceiver event: %#v", event)
msg := &Message{Raw: &event, Team: m.Credentials.Team}
m.parseMessage(msg)
// check if we didn't empty the message
if msg.Text != "" {
m.MessageChan <- msg
continue
}
// if we have file attached but the message is empty, also send it
if msg.Post != nil {
if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" {
m.MessageChan <- msg
continue
}
}
if m.allevents {
m.MessageChan <- msg
continue
}
switch msg.Raw.Event {
case model.WEBSOCKET_EVENT_USER_ADDED,
model.WEBSOCKET_EVENT_USER_REMOVED,
model.WEBSOCKET_EVENT_CHANNEL_CREATED,
model.WEBSOCKET_EVENT_CHANNEL_DELETED:
m.MessageChan <- msg
continue
}
}
var response model.WebSocketResponse
if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() {
m.logger.Debugf("WsReceiver response: %#v", response)
m.parseResponse(response)
}
}
}
// StatusLoop implements a ping-cycle that ensures that the connection to the chat servers
// remains alive. In case of a disconnect it will try to reconnect. A call to this method
// is blocking until the 'WsQuite' field of the MMClient object is set to 'true'.
func (m *MMClient) StatusLoop() {
retries := 0
backoff := time.Second * 60
if m.OnWsConnect != nil {
m.OnWsConnect()
}
m.logger.Debug("StatusLoop:", m.OnWsConnect != nil)
for {
if m.WsQuit {
return
}
if m.WsConnected {
if err := m.checkAlive(); err != nil {
m.logger.Errorf("Connection is not alive: %#v", err)
}
select {
case <-m.WsPingChan:
m.logger.Debug("WS PONG received")
backoff = time.Second * 60
case <-time.After(time.Second * 5):
if retries > 3 {
m.logger.Debug("StatusLoop() timeout")
m.Logout()
m.WsQuit = false
err := m.Login()
if err != nil {
m.logger.Errorf("Login failed: %#v", err)
break
}
if m.OnWsConnect != nil {
m.OnWsConnect()
}
go m.WsReceiver()
} else {
retries++
backoff = time.Second * 5
}
}
}
time.Sleep(backoff)
}
}

View File

@ -1,207 +0,0 @@
package matterclient
import (
"strings"
"github.com/mattermost/mattermost-server/v5/model"
)
func (m *MMClient) parseActionPost(rmsg *Message) {
// add post to cache, if it already exists don't relay this again.
// this should fix reposts
if ok, _ := m.lruCache.ContainsOrAdd(digestString(rmsg.Raw.Data["post"].(string)), true); ok && rmsg.Raw.Event != model.WEBSOCKET_EVENT_POST_DELETED {
m.logger.Debugf("message %#v in cache, not processing again", rmsg.Raw.Data["post"].(string))
rmsg.Text = ""
return
}
data := model.PostFromJson(strings.NewReader(rmsg.Raw.Data["post"].(string)))
// we don't have the user, refresh the userlist
if m.GetUser(data.UserId) == nil {
m.logger.Infof("User '%v' is not known, ignoring message '%#v'",
data.UserId, data)
return
}
rmsg.Username = m.GetUserName(data.UserId)
rmsg.Channel = m.GetChannelName(data.ChannelId)
rmsg.UserID = data.UserId
rmsg.Type = data.Type
teamid, _ := rmsg.Raw.Data["team_id"].(string)
// edit messsages have no team_id for some reason
if teamid == "" {
// we can find the team_id from the channelid
teamid = m.GetChannelTeamId(data.ChannelId)
rmsg.Raw.Data["team_id"] = teamid
}
if teamid != "" {
rmsg.Team = m.GetTeamName(teamid)
}
// direct message
if rmsg.Raw.Data["channel_type"] == "D" {
rmsg.Channel = m.GetUser(data.UserId).Username
}
rmsg.Text = data.Message
rmsg.Post = data
}
func (m *MMClient) parseMessage(rmsg *Message) {
switch rmsg.Raw.Event {
case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED, model.WEBSOCKET_EVENT_POST_DELETED:
m.parseActionPost(rmsg)
case "user_updated":
user := rmsg.Raw.Data["user"].(map[string]interface{})
if _, ok := user["id"].(string); ok {
m.UpdateUser(user["id"].(string))
}
case "group_added":
if err := m.UpdateChannels(); err != nil {
m.logger.Errorf("failed to update channels: %#v", err)
}
/*
case model.ACTION_USER_REMOVED:
m.handleWsActionUserRemoved(&rmsg)
case model.ACTION_USER_ADDED:
m.handleWsActionUserAdded(&rmsg)
*/
}
}
func (m *MMClient) parseResponse(rmsg model.WebSocketResponse) {
if rmsg.Data != nil {
// ping reply
if rmsg.Data["text"].(string) == "pong" {
m.WsPingChan <- &rmsg
}
}
}
func (m *MMClient) DeleteMessage(postId string) error { //nolint:golint
_, resp := m.Client.DeletePost(postId)
if resp.Error != nil {
return resp.Error
}
return nil
}
func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint
post := &model.Post{Message: text, Id: postId}
res, resp := m.Client.UpdatePost(postId, post)
if resp.Error != nil {
return "", resp.Error
}
return res.Id, nil
}
func (m *MMClient) GetFileLinks(filenames []string) []string {
uriScheme := "https://"
if m.NoTLS {
uriScheme = "http://"
}
var output []string
for _, f := range filenames {
res, resp := m.Client.GetFileLink(f)
if resp.Error != nil {
// public links is probably disabled, create the link ourselves
output = append(output, uriScheme+m.Credentials.Server+model.API_URL_SUFFIX_V4+"/files/"+f)
continue
}
output = append(output, res)
}
return output
}
func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList { //nolint:golint
res, resp := m.Client.GetPostsForChannel(channelId, 0, limit, "", true)
if resp.Error != nil {
return nil
}
return res
}
func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList { //nolint:golint
res, resp := m.Client.GetPostsSince(channelId, time, true)
if resp.Error != nil {
return nil
}
return res
}
func (m *MMClient) GetPublicLink(filename string) string {
res, resp := m.Client.GetFileLink(filename)
if resp.Error != nil {
return ""
}
return res
}
func (m *MMClient) GetPublicLinks(filenames []string) []string {
var output []string
for _, f := range filenames {
res, resp := m.Client.GetFileLink(f)
if resp.Error != nil {
continue
}
output = append(output, res)
}
return output
}
func (m *MMClient) PostMessage(channelId string, text string, rootId string) (string, error) { //nolint:golint
post := &model.Post{ChannelId: channelId, Message: text, RootId: rootId}
res, resp := m.Client.CreatePost(post)
if resp.Error != nil {
return "", resp.Error
}
return res.Id, nil
}
func (m *MMClient) PostMessageWithFiles(channelId string, text string, rootId string, fileIds []string) (string, error) { //nolint:golint
post := &model.Post{ChannelId: channelId, Message: text, RootId: rootId, FileIds: fileIds}
res, resp := m.Client.CreatePost(post)
if resp.Error != nil {
return "", resp.Error
}
return res.Id, nil
}
func (m *MMClient) SearchPosts(query string) *model.PostList {
res, resp := m.Client.SearchPosts(m.Team.Id, query, false)
if resp.Error != nil {
return nil
}
return res
}
// SendDirectMessage sends a direct message to specified user
func (m *MMClient) SendDirectMessage(toUserId string, msg string, rootId string) { //nolint:golint
m.SendDirectMessageProps(toUserId, msg, rootId, nil)
}
func (m *MMClient) SendDirectMessageProps(toUserId string, msg string, rootId string, props map[string]interface{}) { //nolint:golint
m.logger.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
// create DM channel (only happens on first message)
_, resp := m.Client.CreateDirectChannel(m.User.Id, toUserId)
if resp.Error != nil {
m.logger.Debugf("SendDirectMessage to %#v failed: %s", toUserId, resp.Error)
return
}
channelName := model.GetDMNameFromIds(toUserId, m.User.Id)
// update our channels
if err := m.UpdateChannels(); err != nil {
m.logger.Errorf("failed to update channels: %#v", err)
}
// build & send the message
msg = strings.Replace(msg, "\r", "", -1)
post := &model.Post{ChannelId: m.GetChannelId(channelName, m.Team.Id), Message: msg, RootId: rootId, Props: props}
m.Client.CreatePost(post)
}
func (m *MMClient) UploadFile(data []byte, channelId string, filename string) (string, error) { //nolint:golint
f, resp := m.Client.UploadFile(data, channelId, filename)
if resp.Error != nil {
return "", resp.Error
}
return f.FileInfos[0].Id, nil
}

View File

@ -1,165 +0,0 @@
package matterclient
import (
"errors"
"time"
"github.com/mattermost/mattermost-server/v5/model"
)
func (m *MMClient) GetNickName(userId string) string { //nolint:golint
user := m.GetUser(userId)
if user != nil {
return user.Nickname
}
return ""
}
func (m *MMClient) GetStatus(userId string) string { //nolint:golint
res, resp := m.Client.GetUserStatus(userId, "")
if resp.Error != nil {
return ""
}
if res.Status == model.STATUS_AWAY {
return "away"
}
if res.Status == model.STATUS_ONLINE {
return "online"
}
return "offline"
}
func (m *MMClient) GetStatuses() map[string]string {
var ids []string
statuses := make(map[string]string)
for id := range m.Users {
ids = append(ids, id)
}
res, resp := m.Client.GetUsersStatusesByIds(ids)
if resp.Error != nil {
return statuses
}
for _, status := range res {
statuses[status.UserId] = "offline"
if status.Status == model.STATUS_AWAY {
statuses[status.UserId] = "away"
}
if status.Status == model.STATUS_ONLINE {
statuses[status.UserId] = "online"
}
}
return statuses
}
func (m *MMClient) GetTeamId() string { //nolint:golint
return m.Team.Id
}
// GetTeamName returns the name of the specified teamId
func (m *MMClient) GetTeamName(teamId string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
for _, t := range m.OtherTeams {
if t.Id == teamId {
return t.Team.Name
}
}
return ""
}
func (m *MMClient) GetUser(userId string) *model.User { //nolint:golint
m.Lock()
defer m.Unlock()
_, ok := m.Users[userId]
if !ok {
res, resp := m.Client.GetUser(userId, "")
if resp.Error != nil {
return nil
}
m.Users[userId] = res
}
return m.Users[userId]
}
func (m *MMClient) GetUserName(userId string) string { //nolint:golint
user := m.GetUser(userId)
if user != nil {
return user.Username
}
return ""
}
func (m *MMClient) GetUsers() map[string]*model.User {
users := make(map[string]*model.User)
m.RLock()
defer m.RUnlock()
for k, v := range m.Users {
users[k] = v
}
return users
}
func (m *MMClient) UpdateUsers() error {
idx := 0
max := 200
mmusers, resp := m.Client.GetUsers(idx, max, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
for len(mmusers) > 0 {
m.Lock()
for _, user := range mmusers {
m.Users[user.Id] = user
}
m.Unlock()
mmusers, resp = m.Client.GetUsers(idx, max, "")
time.Sleep(time.Millisecond * 300)
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
idx++
}
return nil
}
func (m *MMClient) UpdateUserNick(nick string) error {
user := m.User
user.Nickname = nick
_, resp := m.Client.UpdateUser(user)
if resp.Error != nil {
return resp.Error
}
return nil
}
func (m *MMClient) UsernamesInChannel(channelId string) []string { //nolint:golint
res, resp := m.Client.GetChannelMembers(channelId, 0, 50000, "")
if resp.Error != nil {
m.logger.Errorf("UsernamesInChannel(%s) failed: %s", channelId, resp.Error)
return []string{}
}
allusers := m.GetUsers()
result := []string{}
for _, member := range *res {
result = append(result, allusers[member.UserId].Nickname)
}
return result
}
func (m *MMClient) UpdateStatus(userId string, status string) error { //nolint:golint
_, resp := m.Client.UpdateUserStatus(userId, &model.Status{Status: status})
if resp.Error != nil {
return resp.Error
}
return nil
}
func (m *MMClient) UpdateUser(userId string) { //nolint:golint
m.Lock()
defer m.Unlock()
res, resp := m.Client.GetUser(userId, "")
if resp.Error != nil {
return
}
m.Users[userId] = res
}

View File

@ -7,8 +7,8 @@ import "filippo.io/edwards25519"
This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives.
Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519).
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255.
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality.
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/internal/edwards25519`/`crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements.

View File

@ -4,7 +4,7 @@
// Package edwards25519 implements group logic for the twisted Edwards curve
//
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
//
// This is better known as the Edwards curve equivalent to Curve25519, and is
// the curve used by the Ed25519 signature scheme.
@ -15,6 +15,6 @@
//
// However, developers who do need to interact with low-level edwards25519
// operations can use this package, which is an extended version of
// crypto/ed25519/internal/edwards25519 from the standard library repackaged as
// crypto/internal/edwards25519 from the standard library repackaged as
// an importable module.
package edwards25519

View File

@ -27,13 +27,13 @@ type projP2 struct {
//
// The zero value is NOT valid, and it may be used only as a receiver.
type Point struct {
// The point is internally represented in extended coordinates (X, Y, Z, T)
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
x, y, z, t field.Element
// Make the type not comparable (i.e. used with == or as a map key), as
// equivalent points can be represented by different Go values.
_ incomparable
// The point is internally represented in extended coordinates (X, Y, Z, T)
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
x, y, z, t field.Element
}
type incomparable [0]func()
@ -148,9 +148,8 @@ func (v *Point) SetBytes(x []byte) (*Point, error) {
// (*field.Element).SetBytes docs) and
// 2) the ones where the x-coordinate is zero and the sign bit is set.
//
// This is consistent with crypto/ed25519/internal/edwards25519. Read more
// at https://hdevalence.ca/blog/2020-10-04-its-25519am, specifically the
// "Canonical A, R" section.
// Read more at https://hdevalence.ca/blog/2020-10-04-its-25519am,
// specifically the "Canonical A, R" section.
y, err := new(field.Element).SetBytes(x)
if err != nil {

View File

@ -5,7 +5,7 @@
package edwards25519
// This file contains additional functionality that is not included in the
// upstream crypto/ed25519/internal/edwards25519 package.
// upstream crypto/internal/edwards25519 package.
import (
"errors"
@ -79,6 +79,12 @@ func isOnCurve(X, Y, Z, T *field.Element) bool {
// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode
// to the same value. If v is the identity point, BytesMontgomery returns 32
// zero bytes, analogously to the X25519 function.
//
// The lack of an inverse operation (such as SetMontgomeryBytes) is deliberate:
// while every valid edwards25519 point has a unique u-coordinate Montgomery
// encoding, X25519 accepts inputs on the quadratic twist, which don't correspond
// to any edwards25519 point, and every other X25519 input corresponds to two
// edwards25519 points.
func (v *Point) BytesMontgomery() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
@ -137,7 +143,7 @@ func (s *Scalar) Invert(t *Scalar) *Scalar {
for i := 0; i < 7; i++ {
table[i+1].Multiply(&table[i], &tt)
}
// Now table = [t**1, t**3, t**7, t**11, t**13, t**15]
// Now table = [t**1, t**3, t**5, t**7, t**9, t**11, t**13, t**15]
// so t**k = t[k/2] for odd k
// To compute the sliding window digits, use the following Sage script:

View File

@ -188,12 +188,13 @@ func (v *Element) Set(a *Element) *Element {
}
// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is
// not of the right length, SetUniformBytes returns nil and an error, and the
// not of the right length, SetBytes returns nil and an error, and the
// receiver is unchanged.
//
// Consistent with RFC 7748, the most significant bit (the high bit of the
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
// are accepted. Note that this is laxer than specified by RFC 8032.
// are accepted. Note that this is laxer than specified by RFC 8032, but
// consistent with most Ed25519 implementations.
func (v *Element) SetBytes(x []byte) (*Element, error) {
if len(x) != 32 {
return nil, errors.New("edwards25519: invalid field element input size")
@ -211,7 +212,7 @@ func (v *Element) SetBytes(x []byte) (*Element, error) {
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
v.l3 &= maskLow51Bits
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
// Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51).
// Note: not bytes 25:33, shift 4, to avoid overread.
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
v.l4 &= maskLow51Bits
@ -394,26 +395,26 @@ var sqrtM1 = &Element{1718705420411056, 234908883556509,
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
// and returns r and 0.
func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) {
var a, b Element
func (r *Element) SqrtRatio(u, v *Element) (R *Element, wasSquare int) {
t0 := new(Element)
// r = (u * v3) * (u * v7)^((p-5)/8)
v2 := a.Square(v)
uv3 := b.Multiply(u, b.Multiply(v2, v))
uv7 := a.Multiply(uv3, a.Square(v2))
r.Multiply(uv3, r.Pow22523(uv7))
v2 := new(Element).Square(v)
uv3 := new(Element).Multiply(u, t0.Multiply(v2, v))
uv7 := new(Element).Multiply(uv3, t0.Square(v2))
rr := new(Element).Multiply(uv3, t0.Pow22523(uv7))
check := a.Multiply(v, a.Square(r)) // check = v * r^2
check := new(Element).Multiply(v, t0.Square(rr)) // check = v * r^2
uNeg := b.Negate(u)
uNeg := new(Element).Negate(u)
correctSignSqrt := check.Equal(u)
flippedSignSqrt := check.Equal(uNeg)
flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1))
flippedSignSqrtI := check.Equal(t0.Multiply(uNeg, sqrtM1))
rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r
rPrime := new(Element).Multiply(rr, sqrtM1) // r_prime = SQRT_M1 * r
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI)
rr.Select(rPrime, rr, flippedSignSqrt|flippedSignSqrtI)
r.Absolute(r) // Choose the nonnegative square root.
r.Absolute(rr) // Choose the nonnegative square root.
return r, correctSignSqrt | flippedSignSqrt
}

View File

@ -1,13 +1,16 @@
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package field
// feMul sets out = a * b. It works like feMulGeneric.
//
//go:noescape
func feMul(out *Element, a *Element, b *Element)
// feSquare sets out = a * a. It works like feSquareGeneric.
//
//go:noescape
func feSquare(out *Element, a *Element)

View File

@ -1,5 +1,6 @@
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
#include "textflag.h"

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm64,gc,!purego
//go:build arm64 && gc && !purego
#include "textflag.h"

50
vendor/filippo.io/edwards25519/field/fe_extra.go generated vendored Normal file
View File

@ -0,0 +1,50 @@
// Copyright (c) 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package field
import "errors"
// This file contains additional functionality that is not included in the
// upstream crypto/ed25519/edwards25519/field package.
// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which
// is reduced modulo the field order. If x is not of the right length,
// SetWideBytes returns nil and an error, and the receiver is unchanged.
//
// SetWideBytes is not necessary to select a uniformly distributed value, and is
// only provided for compatibility: SetBytes can be used instead as the chance
// of bias is less than 2⁻²⁵⁰.
func (v *Element) SetWideBytes(x []byte) (*Element, error) {
if len(x) != 64 {
return nil, errors.New("edwards25519: invalid SetWideBytes input size")
}
// Split the 64 bytes into two elements, and extract the most significant
// bit of each, which is ignored by SetBytes.
lo, _ := new(Element).SetBytes(x[:32])
loMSB := uint64(x[31] >> 7)
hi, _ := new(Element).SetBytes(x[32:])
hiMSB := uint64(x[63] >> 7)
// The output we want is
//
// v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹
//
// which applying the reduction identity comes out to
//
// v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19²
//
// l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value
// (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value
// (hiMSB * 2 * 19²), so it fits in a uint64.
v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19
v.l1 = lo.l1 + hi.l1*2*19
v.l2 = lo.l2 + hi.l2*2*19
v.l3 = lo.l3 + hi.l3*2*19
v.l4 = lo.l4 + hi.l4*2*19
return v.carryPropagate(), nil
}

View File

@ -156,7 +156,7 @@ func feMulGeneric(v, a, b *Element) {
rr4 := r4.lo&maskLow51Bits + c3
// Now all coefficients fit into 64-bit registers but are still too large to
// be passed around as a Element. We therefore do one last carry chain,
// be passed around as an Element. We therefore do one last carry chain,
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
*v = Element{rr0, rr1, rr2, rr3, rr4}
v.carryPropagate()
@ -245,7 +245,7 @@ func feSquareGeneric(v, a *Element) {
v.carryPropagate()
}
// carryPropagate brings the limbs below 52 bits by applying the reduction
// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
func (v *Element) carryPropagateGeneric() *Element {
c0 := v.l0 >> 51
@ -254,6 +254,8 @@ func (v *Element) carryPropagateGeneric() *Element {
c3 := v.l3 >> 51
c4 := v.l4 >> 51
// c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and
// the final l0 will be at most 52 bits. Similarly for the rest.
v.l0 = v.l0&maskLow51Bits + c4*19
v.l1 = v.l1&maskLow51Bits + c0
v.l2 = v.l2&maskLow51Bits + c1

File diff suppressed because it is too large Load Diff

1147
vendor/filippo.io/edwards25519/scalar_fiat.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -38,9 +38,9 @@ func (v *projLookupTable) FromP3(q *Point) {
tmpP3 := Point{}
tmpP1xP1 := projP1xP1{}
for i := 0; i < 7; i++ {
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
// Compute (i+1)*Q as Q + i*Q and convert to a projCached
// This is needlessly complicated because the API has explicit
// recievers instead of creating stack objects and relying on RVO
// receivers instead of creating stack objects and relying on RVO
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
}
}
@ -53,7 +53,7 @@ func (v *affineLookupTable) FromP3(q *Point) {
tmpP3 := Point{}
tmpP1xP1 := projP1xP1{}
for i := 0; i < 7; i++ {
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
// Compute (i+1)*Q as Q + i*Q and convert to affineCached
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i])))
}
}

View File

@ -1,3 +0,0 @@
.idea
/test
app.yaml

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,154 +0,0 @@
# gitter
Gitter API in Go
https://developer.gitter.im
#### Install
`go get github.com/sromku/go-gitter`
- [Initialize](#initialize)
- [Users](#users)
- [Rooms](#rooms)
- [Messages](#messages)
- [Stream](#stream)
- [Faye (Experimental)](#faye-experimental)
- [Debug](#debug)
- [App Engine](#app-engine)
##### Initialize
``` Go
api := gitter.New("YOUR_ACCESS_TOKEN")
```
##### Users
- Get current user
``` Go
user, err := api.GetUser()
```
##### Rooms
- Get all rooms
``` Go
rooms, err := api.GetRooms()
```
- Get room by id
``` Go
room, err := api.GetRoom("roomID")
```
- Get rooms of some user
``` Go
rooms, err := api.GetRooms("userID")
```
- Join room
``` Go
room, err := api.JoinRoom("roomID", "userID")
```
- Leave room
``` Go
room, err := api.LeaveRoom("roomID", "userID")
```
- Get room id
``` Go
id, err := api.GetRoomId("room/uri")
```
- Search gitter rooms
``` Go
rooms, err := api.SearchRooms("search/string")
```
##### Messages
- Get messages of room
``` Go
messages, err := api.GetMessages("roomID", nil)
```
- Get one message
``` Go
message, err := api.GetMessage("roomID", "messageID")
```
- Send message
``` Go
err := api.SendMessage("roomID", "free chat text")
```
##### Stream
Create stream to the room and start listening to incoming messages
``` Go
stream := api.Stream(room.Id)
go api.Listen(stream)
for {
event := <-stream.Event
switch ev := event.Data.(type) {
case *gitter.MessageReceived:
fmt.Println(ev.Message.From.Username + ": " + ev.Message.Text)
case *gitter.GitterConnectionClosed:
// connection was closed
}
}
```
Close stream connection
``` Go
stream.Close()
```
##### Faye (Experimental)
``` Go
faye := api.Faye(room.ID)
go faye.Listen()
for {
event := <-faye.Event
switch ev := event.Data.(type) {
case *gitter.MessageReceived:
fmt.Println(ev.Message.From.Username + ": " + ev.Message.Text)
case *gitter.GitterConnectionClosed: //this one is never called in Faye
// connection was closed
}
}
```
##### Debug
You can print the internal errors by enabling debug to true
``` Go
api.SetDebug(true, nil)
```
You can also define your own `io.Writer` in case you want to persist the logs somewhere.
For example keeping the errors on file
``` Go
logFile, err := os.Create("gitter.log")
api.SetDebug(true, logFile)
```
##### App Engine
Initialize app engine client and continue as usual
``` Go
c := appengine.NewContext(r)
client := urlfetch.Client(c)
api := gitter.New("YOUR_ACCESS_TOKEN")
api.SetClient(client)
```
[Documentation](https://godoc.org/github.com/sromku/go-gitter)

View File

@ -1,70 +0,0 @@
package gitter
import (
"encoding/json"
"fmt"
"github.com/mrexodia/wray"
)
type Faye struct {
endpoint string
Event chan Event
client *wray.FayeClient
gitter *Gitter
}
func (gitter *Gitter) Faye(roomID string) *Faye {
wray.RegisterTransports([]wray.Transport{
&wray.HttpTransport{
SendHook: func(data map[string]interface{}) {
if channel, ok := data["channel"]; ok && channel == "/meta/handshake" {
data["ext"] = map[string]interface{}{"token": gitter.config.token}
}
},
},
})
return &Faye{
endpoint: "/api/v1/rooms/" + roomID + "/chatMessages",
Event: make(chan Event),
client: wray.NewFayeClient(fayeBaseURL),
gitter: gitter,
}
}
func (faye *Faye) Listen() {
defer faye.destroy()
faye.client.Subscribe(faye.endpoint, false, func(message wray.Message) {
dataBytes, err := json.Marshal(message.Data["model"])
if err != nil {
fmt.Printf("JSON Marshal error: %v\n", err)
return
}
var gitterMessage Message
err = json.Unmarshal(dataBytes, &gitterMessage)
if err != nil {
fmt.Printf("JSON Unmarshal error: %v\n", err)
return
}
faye.Event <- Event{
Data: &MessageReceived{
Message: gitterMessage,
},
}
})
//TODO: this might be needed in the future
/*go func() {
for {
faye.client.Publish("/api/v1/ping2", map[string]interface{}{"reason": "ping"})
time.Sleep(60 * time.Second)
}
}()*/
faye.client.Listen()
}
func (faye *Faye) destroy() {
close(faye.Event)
}

View File

@ -1,527 +0,0 @@
// Package gitter is a Go client library for the Gitter API.
//
// Author: sromku
package gitter
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"strconv"
"time"
"github.com/mreiferson/go-httpclient"
)
var (
apiBaseURL = "https://api.gitter.im/v1/"
streamBaseURL = "https://stream.gitter.im/v1/"
fayeBaseURL = "https://ws.gitter.im/faye"
)
type Gitter struct {
config struct {
apiBaseURL string
streamBaseURL string
token string
client *http.Client
}
debug bool
logWriter io.Writer
}
// New initializes the Gitter API client
//
// For example:
// api := gitter.New("YOUR_ACCESS_TOKEN")
func New(token string) *Gitter {
transport := &httpclient.Transport{
ConnectTimeout: 5 * time.Second,
ReadWriteTimeout: 40 * time.Second,
}
defer transport.Close()
s := &Gitter{}
s.config.apiBaseURL = apiBaseURL
s.config.streamBaseURL = streamBaseURL
s.config.token = token
s.config.client = &http.Client{
Transport: transport,
}
return s
}
// SetClient sets a custom http client. Can be useful in App Engine case.
func (gitter *Gitter) SetClient(client *http.Client) {
gitter.config.client = client
}
// GetUser returns the current user
func (gitter *Gitter) GetUser() (*User, error) {
var users []User
response, err := gitter.get(gitter.config.apiBaseURL + "user")
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &users)
if err != nil {
gitter.log(err)
return nil, err
}
if len(users) > 0 {
return &users[0], nil
}
err = APIError{What: "Failed to retrieve current user"}
gitter.log(err)
return nil, err
}
// GetUserRooms returns a list of Rooms the user is part of
func (gitter *Gitter) GetUserRooms(userID string) ([]Room, error) {
var rooms []Room
response, err := gitter.get(gitter.config.apiBaseURL + "user/" + userID + "/rooms")
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &rooms)
if err != nil {
gitter.log(err)
return nil, err
}
return rooms, nil
}
// GetRooms returns a list of rooms the current user is in
func (gitter *Gitter) GetRooms() ([]Room, error) {
var rooms []Room
response, err := gitter.get(gitter.config.apiBaseURL + "rooms")
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &rooms)
if err != nil {
gitter.log(err)
return nil, err
}
return rooms, nil
}
// GetUsersInRoom returns the users in the room with the passed id
func (gitter *Gitter) GetUsersInRoom(roomID string) ([]User, error) {
var users []User
response, err := gitter.get(gitter.config.apiBaseURL + "rooms/" + roomID + "/users")
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &users)
if err != nil {
gitter.log(err)
return nil, err
}
return users, nil
}
// GetRoom returns a room with the passed id
func (gitter *Gitter) GetRoom(roomID string) (*Room, error) {
var room Room
response, err := gitter.get(gitter.config.apiBaseURL + "rooms/" + roomID)
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &room)
if err != nil {
gitter.log(err)
return nil, err
}
return &room, nil
}
// GetMessages returns a list of messages in a room.
// Pagination is optional. You can pass nil or specific pagination params.
func (gitter *Gitter) GetMessages(roomID string, params *Pagination) ([]Message, error) {
var messages []Message
url := gitter.config.apiBaseURL + "rooms/" + roomID + "/chatMessages"
if params != nil {
url += "?" + params.encode()
}
response, err := gitter.get(url)
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &messages)
if err != nil {
gitter.log(err)
return nil, err
}
return messages, nil
}
// GetMessage returns a message in a room.
func (gitter *Gitter) GetMessage(roomID, messageID string) (*Message, error) {
var message Message
response, err := gitter.get(gitter.config.apiBaseURL + "rooms/" + roomID + "/chatMessages/" + messageID)
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &message)
if err != nil {
gitter.log(err)
return nil, err
}
return &message, nil
}
// SendMessage sends a message to a room
func (gitter *Gitter) SendMessage(roomID, text string) (*Message, error) {
message := Message{Text: text}
body, _ := json.Marshal(message)
response, err := gitter.post(gitter.config.apiBaseURL+"rooms/"+roomID+"/chatMessages", body)
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &message)
if err != nil {
gitter.log(err)
return nil, err
}
return &message, nil
}
// UpdateMessage updates a message in a room
func (gitter *Gitter) UpdateMessage(roomID, msgID, text string) (*Message, error) {
message := Message{Text: text}
body, _ := json.Marshal(message)
response, err := gitter.put(gitter.config.apiBaseURL+"rooms/"+roomID+"/chatMessages/"+msgID, body)
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &message)
if err != nil {
gitter.log(err)
return nil, err
}
return &message, nil
}
// JoinRoom joins a room
func (gitter *Gitter) JoinRoom(roomID, userID string) (*Room, error) {
message := Room{ID: roomID}
body, _ := json.Marshal(message)
response, err := gitter.post(gitter.config.apiBaseURL+"user/"+userID+"/rooms", body)
if err != nil {
gitter.log(err)
return nil, err
}
var room Room
err = json.Unmarshal(response, &room)
if err != nil {
gitter.log(err)
return nil, err
}
return &room, nil
}
// LeaveRoom removes a user from the room
func (gitter *Gitter) LeaveRoom(roomID, userID string) error {
_, err := gitter.delete(gitter.config.apiBaseURL + "rooms/" + roomID + "/users/" + userID)
if err != nil {
gitter.log(err)
return err
}
return nil
}
// SetDebug traces errors if it's set to true.
func (gitter *Gitter) SetDebug(debug bool, logWriter io.Writer) {
gitter.debug = debug
gitter.logWriter = logWriter
}
// SearchRooms queries the Rooms resources of gitter API
func (gitter *Gitter) SearchRooms(room string) ([]Room, error) {
var rooms struct {
Results []Room `json:"results"`
}
response, err := gitter.get(gitter.config.apiBaseURL + "rooms?q=" + room)
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &rooms)
if err != nil {
gitter.log(err)
return nil, err
}
return rooms.Results, nil
}
// GetRoomId returns the room ID of a given URI
func (gitter *Gitter) GetRoomId(uri string) (string, error) {
rooms, err := gitter.SearchRooms(uri)
if err != nil {
gitter.log(err)
return "", err
}
for _, element := range rooms {
if element.URI == uri {
return element.ID, nil
}
}
return "", APIError{What: "Room not found."}
}
// Pagination params
type Pagination struct {
// Skip n messages
Skip int
// Get messages before beforeId
BeforeID string
// Get messages after afterId
AfterID string
// Maximum number of messages to return
Limit int
// Search query
Query string
}
func (messageParams *Pagination) encode() string {
values := url.Values{}
if messageParams.AfterID != "" {
values.Add("afterId", messageParams.AfterID)
}
if messageParams.BeforeID != "" {
values.Add("beforeId", messageParams.BeforeID)
}
if messageParams.Skip > 0 {
values.Add("skip", strconv.Itoa(messageParams.Skip))
}
if messageParams.Limit > 0 {
values.Add("limit", strconv.Itoa(messageParams.Limit))
}
return values.Encode()
}
func (gitter *Gitter) getResponse(url string, stream *Stream) (*http.Response, error) {
r, err := http.NewRequest("GET", url, nil)
if err != nil {
gitter.log(err)
return nil, err
}
r.Header.Set("Content-Type", "application/json")
r.Header.Set("Accept", "application/json")
r.Header.Set("Authorization", "Bearer "+gitter.config.token)
if stream != nil {
stream.streamConnection.request = r
}
response, err := gitter.config.client.Do(r)
if err != nil {
gitter.log(err)
return nil, err
}
return response, nil
}
func (gitter *Gitter) get(url string) ([]byte, error) {
resp, err := gitter.getResponse(url, nil)
if err != nil {
gitter.log(err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = APIError{What: fmt.Sprintf("Status code: %v", resp.StatusCode)}
gitter.log(err)
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
gitter.log(err)
return nil, err
}
return body, nil
}
func (gitter *Gitter) post(url string, body []byte) ([]byte, error) {
r, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
gitter.log(err)
return nil, err
}
r.Header.Set("Content-Type", "application/json")
r.Header.Set("Accept", "application/json")
r.Header.Set("Authorization", "Bearer "+gitter.config.token)
resp, err := gitter.config.client.Do(r)
if err != nil {
gitter.log(err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = APIError{What: fmt.Sprintf("Status code: %v", resp.StatusCode)}
gitter.log(err)
return nil, err
}
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
gitter.log(err)
return nil, err
}
return result, nil
}
func (gitter *Gitter) put(url string, body []byte) ([]byte, error) {
r, err := http.NewRequest("PUT", url, bytes.NewBuffer(body))
if err != nil {
gitter.log(err)
return nil, err
}
r.Header.Set("Content-Type", "application/json")
r.Header.Set("Accept", "application/json")
r.Header.Set("Authorization", "Bearer "+gitter.config.token)
resp, err := gitter.config.client.Do(r)
if err != nil {
gitter.log(err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = APIError{What: fmt.Sprintf("Status code: %v", resp.StatusCode)}
gitter.log(err)
return nil, err
}
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
gitter.log(err)
return nil, err
}
return result, nil
}
func (gitter *Gitter) delete(url string) ([]byte, error) {
r, err := http.NewRequest("delete", url, nil)
if err != nil {
gitter.log(err)
return nil, err
}
r.Header.Set("Content-Type", "application/json")
r.Header.Set("Accept", "application/json")
r.Header.Set("Authorization", "Bearer "+gitter.config.token)
resp, err := gitter.config.client.Do(r)
if err != nil {
gitter.log(err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = APIError{What: fmt.Sprintf("Status code: %v", resp.StatusCode)}
gitter.log(err)
return nil, err
}
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
gitter.log(err)
return nil, err
}
return result, nil
}
func (gitter *Gitter) log(a interface{}) {
if gitter.debug {
log.Println(a)
if gitter.logWriter != nil {
timestamp := time.Now().Format(time.RFC3339)
msg := fmt.Sprintf("%v: %v", timestamp, a)
fmt.Fprintln(gitter.logWriter, msg)
}
}
}
// APIError holds data of errors returned from the API.
type APIError struct {
What string
}
func (e APIError) Error() string {
return fmt.Sprintf("%v", e.What)
}

View File

@ -1,142 +0,0 @@
package gitter
import "time"
// A Room in Gitter can represent a GitHub Organization, a GitHub Repository, a Gitter Channel or a One-to-one conversation.
// In the case of the Organizations and Repositories, the access control policies are inherited from GitHub.
type Room struct {
// Room ID
ID string `json:"id"`
// Room name
Name string `json:"name"`
// Room topic. (default: GitHub repo description)
Topic string `json:"topic"`
// Room URI on Gitter
URI string `json:"uri"`
// Indicates if the room is a one-to-one chat
OneToOne bool `json:"oneToOne"`
// Count of users in the room
UserCount int `json:"userCount"`
// Number of unread messages for the current user
UnreadItems int `json:"unreadItems"`
// Number of unread mentions for the current user
Mentions int `json:"mentions"`
// Last time the current user accessed the room in ISO format
LastAccessTime time.Time `json:"lastAccessTime"`
// Indicates if the current user has disabled notifications
Lurk bool `json:"lurk"`
// Path to the room on gitter
URL string `json:"url"`
// Type of the room
// - ORG: A room that represents a GitHub Organization.
// - REPO: A room that represents a GitHub Repository.
// - ONETOONE: A one-to-one chat.
// - ORG_CHANNEL: A Gitter channel nested under a GitHub Organization.
// - REPO_CHANNEL A Gitter channel nested under a GitHub Repository.
// - USER_CHANNEL A Gitter channel nested under a GitHub User.
GithubType string `json:"githubType"`
// Tags that define the room
Tags []string `json:"tags"`
RoomMember bool `json:"roomMember"`
// Room version.
Version int `json:"v"`
}
type User struct {
// Gitter User ID
ID string `json:"id"`
// Gitter/GitHub username
Username string `json:"username"`
// Gitter/GitHub user real name
DisplayName string `json:"displayName"`
// Path to the user on Gitter
URL string `json:"url"`
// User avatar URI (small)
AvatarURLSmall string `json:"avatarUrlSmall"`
// User avatar URI (medium)
AvatarURLMedium string `json:"avatarUrlMedium"`
}
type Message struct {
// ID of the message
ID string `json:"id"`
// Original message in plain-text/markdown
Text string `json:"text"`
// HTML formatted message
HTML string `json:"html"`
// ISO formatted date of the message
Sent time.Time `json:"sent"`
// ISO formatted date of the message if edited
EditedAt time.Time `json:"editedAt"`
// User that sent the message
From User `json:"fromUser"`
// Boolean that indicates if the current user has read the message.
Unread bool `json:"unread"`
// Number of users that have read the message
ReadBy int `json:"readBy"`
// List of URLs present in the message
Urls []URL `json:"urls"`
// List of @Mentions in the message
Mentions []Mention `json:"mentions"`
// List of #Issues referenced in the message
Issues []Issue `json:"issues"`
// Version
Version int `json:"v"`
}
// Mention holds data about mentioned user in the message
type Mention struct {
// User's username
ScreenName string `json:"screenName"`
// Gitter User ID
UserID string `json:"userID"`
}
// Issue references issue in the message
type Issue struct {
// Issue number
Number string `json:"number"`
}
// URL presented in the message
type URL struct {
// URL
URL string `json:"url"`
}

View File

@ -1,217 +0,0 @@
package gitter
import (
"bufio"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/mreiferson/go-httpclient"
)
var defaultConnectionWaitTime time.Duration = 3000 // millis
var defaultConnectionMaxRetries = 5
// Stream initialize stream
func (gitter *Gitter) Stream(roomID string) *Stream {
return &Stream{
url: streamBaseURL + "rooms/" + roomID + "/chatMessages",
Event: make(chan Event),
gitter: gitter,
streamConnection: gitter.newStreamConnection(
defaultConnectionWaitTime,
defaultConnectionMaxRetries),
}
}
// Implemented to conform with https://developer.gitter.im/docs/streaming-api
func (gitter *Gitter) Listen(stream *Stream) {
defer stream.destroy()
var reader *bufio.Reader
var gitterMessage Message
lastKeepalive := time.Now().Unix()
// connect
stream.connect()
Loop:
for {
// if closed then stop trying
if stream.isClosed() {
stream.Event <- Event{
Data: &GitterConnectionClosed{},
}
break Loop
}
resp := stream.getResponse()
if resp.StatusCode != 200 {
gitter.log(fmt.Sprintf("Unexpected response code %v", resp.StatusCode))
continue
}
//"The JSON stream returns messages as JSON objects that are delimited by carriage return (\r)" <- Not true crap it's (\n) only
reader = bufio.NewReader(resp.Body)
line, err := reader.ReadBytes('\n')
if err != nil {
gitter.log("ReadBytes error: " + err.Error())
stream.connect()
continue
}
//Check if the line only consists of whitespace
onlyWhitespace := true
for _, b := range line {
if b != ' ' && b != '\t' && b != '\r' && b != '\n' {
onlyWhitespace = false
}
}
if onlyWhitespace {
//"Parsers must be tolerant of occasional extra newline characters placed between messages."
currentKeepalive := time.Now().Unix() //interesting behavior of 100+ keepalives per seconds was observed
if currentKeepalive-lastKeepalive > 10 {
lastKeepalive = currentKeepalive
gitter.log("Keepalive was received")
}
continue
} else if stream.isClosed() {
gitter.log("Stream closed")
continue
}
// unmarshal the streamed data
err = json.Unmarshal(line, &gitterMessage)
if err != nil {
gitter.log("JSON Unmarshal error: " + err.Error())
continue
}
// we are here, then we got the good message. pipe it forward.
stream.Event <- Event{
Data: &MessageReceived{
Message: gitterMessage,
},
}
}
gitter.log("Listening was completed")
}
// Stream holds stream data.
type Stream struct {
url string
Event chan Event
streamConnection *streamConnection
gitter *Gitter
}
func (stream *Stream) destroy() {
close(stream.Event)
stream.streamConnection.currentRetries = 0
}
type Event struct {
Data interface{}
}
type GitterConnectionClosed struct {
}
type MessageReceived struct {
Message Message
}
// connect and try to reconnect with
func (stream *Stream) connect() {
if stream.streamConnection.retries == stream.streamConnection.currentRetries {
stream.Close()
stream.gitter.log("Number of retries exceeded the max retries number, we are done here")
return
}
res, err := stream.gitter.getResponse(stream.url, stream)
if err != nil || res.StatusCode != 200 {
stream.gitter.log("Failed to get response, trying reconnect")
if res != nil {
stream.gitter.log(fmt.Sprintf("Status code: %v", res.StatusCode))
}
stream.gitter.log(err)
// sleep and wait
stream.streamConnection.currentRetries++
time.Sleep(time.Millisecond * stream.streamConnection.wait * time.Duration(stream.streamConnection.currentRetries))
// connect again
stream.Close()
stream.connect()
} else {
stream.gitter.log("Response was received")
stream.streamConnection.currentRetries = 0
stream.streamConnection.closed = false
stream.streamConnection.response = res
}
}
type streamConnection struct {
// connection was closed
closed bool
// wait time till next try
wait time.Duration
// max tries to recover
retries int
// current streamed response
response *http.Response
// current request
request *http.Request
// current status
currentRetries int
}
// Close the stream connection and stop receiving streamed data
func (stream *Stream) Close() {
conn := stream.streamConnection
conn.closed = true
if conn.response != nil {
stream.gitter.log("Stream connection close response")
defer conn.response.Body.Close()
}
if conn.request != nil {
stream.gitter.log("Stream connection close request")
switch transport := stream.gitter.config.client.Transport.(type) {
case *httpclient.Transport:
transport.CancelRequest(conn.request)
default:
}
}
}
func (stream *Stream) isClosed() bool {
return stream.streamConnection.closed
}
func (stream *Stream) getResponse() *http.Response {
return stream.streamConnection.response
}
// Optional, set stream connection properties
// wait - time in milliseconds of waiting between reconnections. Will grow exponentially.
// retries - number of reconnections retries before dropping the stream.
func (gitter *Gitter) newStreamConnection(wait time.Duration, retries int) *streamConnection {
return &streamConnection{
closed: true,
wait: wait,
retries: retries,
}
}

View File

@ -1,30 +0,0 @@
package gitter
import (
"net/http"
"net/http/httptest"
"net/url"
)
var (
mux *http.ServeMux
gitter *Gitter
server *httptest.Server
)
func setup() {
mux = http.NewServeMux()
server = httptest.NewServer(mux)
gitter = New("abc")
// Fake the API and Stream base URLs by using the test
// server URL instead.
url, _ := url.Parse(server.URL)
gitter.config.apiBaseURL = url.String() + "/"
gitter.config.streamBaseURL = url.String() + "/"
}
func teardown() {
server.Close()
}

View File

@ -1,9 +1,11 @@
---
run:
timeout: 5m
linters:
disable-all: true
enable:
- bodyclose
- deadcode
- errcheck
- gochecknoglobals
- goconst
@ -19,13 +21,11 @@ linters:
- nakedret
- prealloc
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
- wsl
- godot
@ -40,7 +40,6 @@ linters:
- makezero
- thelper
- predeclared
- ifshort
- revive
- durationcheck
- gomoddirectives
@ -57,9 +56,31 @@ linters:
- grouper
- decorder
- containedctx
# - execinquery # FIXME: panic in 1.46.0
- nosprintfhostport
- usestdlibvars
- interfacebloat
- reassign
- testableexamples
- gocheckcompilerdirectives
- asasalint
- musttag
- gosmopolitan
- mirror
- tagalign
- gochecksumtype
- inamedparam
- perfsprint
- sloglint
- testifylint
# - musttag # TODO: need update golangci-lint
# - wrapcheck # TODO: v3 Fix
# - testpackage # TODO: Fix testpackage
# - noctx # TODO: Fix noctx
@ -90,11 +111,26 @@ linters:
# - errchkjson
# - maintidx
# - nonamedreturns
# - nosnakecase
# - execinquery
# - logrlint
# - dupword
# - ginkgolinter
# - zerologlint
# - protogetter
# - spancheck
# depricated
# - maligned
# - interfacer
# - golint
# - ifshort
# - deadcode
# - structcheck
# - varcheck
issues:
exclude-rules:
@ -114,4 +150,14 @@ issues:
- stylecheck
text: "ST1003:.*(Ts|ts).*TS"
- linters:
- gosec
text: "G307:"
exclude-use-default: false
linters-settings:
testifylint:
enable-all: true
disable:
- require-error # f func false positive

View File

@ -6,7 +6,7 @@
Требования:
- [Go 1.16+](https://golang.org/doc/install)
- [Go 1.21+](https://golang.org/doc/install)
- [golangci-lint](https://github.com/golangci/golangci-lint)
- [global .gitignore](https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore)
@ -54,17 +54,17 @@ go test ./...
```json
{
"go.testEnvVars": {
"SERVICE_TOKEN": "",
"WIDGET_TOKEN": "",
"MARUSIA_TOKEN": "",
"GROUP_TOKEN": "",
"CLIENT_SECRET": "",
"USER_TOKEN": "",
"CLIENT_ID": "123456",
"GROUP_ID": "123456",
"ACCOUNT_ID": "123456"
}
"go.testEnvVars": {
"SERVICE_TOKEN": "",
"WIDGET_TOKEN": "",
"MARUSIA_TOKEN": "",
"GROUP_TOKEN": "",
"CLIENT_SECRET": "",
"USER_TOKEN": "",
"CLIENT_ID": "123456",
"GROUP_ID": "123456",
"ACCOUNT_ID": "123456"
}
}
```
@ -88,7 +88,4 @@ git push origin <name_of_your_new_branch>
```
Затем откройте [pull request](https://github.com/SevereCloud/vksdk/pulls)
с веткой:
- `master` если это багфикс
- `dev-v1.2.3` если это новая фича
с веткой master

View File

@ -1,13 +1,13 @@
# API
[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/api)](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/first_guide)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://dev.vk.com/ru/api/getting-started)
Данная библиотека поддерживает версию API **5.131**.
## Запросы
В начале необходимо инициализировать api с помощью [ключа доступа](https://vk.com/dev/access_token):
В начале необходимо инициализировать api с помощью [ключа доступа](https://dev.vk.com/ru/api/access-token/getting-started):
```go
vk := api.NewVK("<TOKEN>")
@ -21,7 +21,7 @@ vk := api.NewVK("<TOKEN>")
Список всех методов можно найти на
[данной странице](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api?tab=doc#VK).
Пример запроса [`users.get`](https://vk.com/dev/users.get)
Пример запроса [`users.get`](https://dev.vk.com/method/users.get)
```go
users, err := vk.UsersGet(api.Params{
@ -52,7 +52,7 @@ res, err = api.MessageSend(b.Params)
### Обработка ошибок
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/errors)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://dev.vk.com/ru/reference/errors)
Обработка ошибок полностью поддерживает методы
[go 1.13](https://blog.golang.org/go1.13-errors)
@ -130,7 +130,7 @@ log.Println("msgpack:", len(r)) // msgpack: 650775
### Запрос любого метода
Пример запроса [users.get](https://vk.com/dev/users.get)
Пример запроса [users.get](https://dev.vk.com/method/users.get)
```go
// Определяем структуру, которую вернет API
@ -153,7 +153,7 @@ log.Print(response)
### Execute
[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/errors)](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api#VK.Execute)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/execute)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://dev.vk.com/ru/method/execute)
Универсальный метод, который позволяет запускать последовательность других
методов, сохраняя и фильтруя промежуточные результаты.
@ -199,7 +199,7 @@ vk.Handler = func(method string, params ...api.Params) (api.Response, error) {
позволяет совершить до 25 обращений к разным методам в рамках одного запроса.
Для методов секции ads действуют собственные ограничения, ознакомиться с ними
Вы можете на [этой странице](https://vk.com/dev/ads_limits).
Вы можете на [этой странице](https://dev.vk.com/ru/method/ads).
Максимальное число обращений к методам секции secure зависит от числа
пользователей, установивших приложение. Если приложение установило меньше 10
@ -236,7 +236,7 @@ vk.Client = client
### Ошибка с Captcha
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/captcha_error)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://dev.vk.com/ru/api/captcha-error)
Если какое-либо действие (например, отправка сообщения) выполняется
пользователем слишком часто, то запрос к API может возвращать ошибку
@ -262,7 +262,7 @@ vk.Client = client
## Загрузка файлов
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/upload_files)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://dev.vk.com/ru/api/upload/overview)
### 1. Загрузка фотографий в альбом
@ -364,7 +364,7 @@ messageInfo, err = vk.UploadChatPhotoCrop(peerID, cropX, cropY, cropWidth, file)
не более 14000px, файл объемом не более 50 МБ, соотношение сторон не менее 1:20.
Если Вы хотите загрузить основную фотографию товара, необходимо передать
параметр `mainPhoto = true`. Если фотография не основная, она не будет обрезаться.
параметр `mainPhoto = true`. Если фотография не основная, она не будет обрезаться.
Без обрезки:
@ -392,7 +392,7 @@ photosPhoto, err = vk.UploadMarketAlbumPhoto(groupID, file)
Допустимые форматы: AVI, MP4, 3GP, MPEG, MOV, MP3, FLV, WMV.
[Параметры](https://vk.com/dev/video.save)
[Параметры](https://dev.vk.com/method/video.save)
```go
videoUploadResponse, err = vk.UploadVideo(params, file)
@ -476,13 +476,13 @@ docsDoc, err = vk.UploadMessagesDoc(peerID, "audio_message", title, tags, file)
не более 10МБ. Формат видео: h264 video, aac audio,
максимальное разрешение 720х1280, 30fps.
Загрузить историю с фотографией. [Параметры](https://vk.com/dev/stories.getPhotoUploadServer)
Загрузить историю с фотографией. [Параметры](https://dev.vk.com/method/stories.getPhotoUploadServer)
```go
uploadInfo, err = vk.UploadStoriesPhoto(params, file)
```
Загрузить историю с видео. [Параметры](https://vk.com/dev/stories.getVideoUploadServer)
Загрузить историю с видео. [Параметры](https://dev.vk.com/method/stories.getVideoUploadServer)
```go
uploadInfo, err = vk.UploadStoriesVideo(params, file)
@ -529,14 +529,14 @@ photo, err = vk.UploadLeadFormsPhoto(file)
```
Полученные данные можно использовать в методах
[leadForms.create](https://vk.com/dev/leadForms.create)
[leadForms.create](https://dev.vk.com/method/leadForms.create)
и
[leadForms.edit](https://vk.com/dev/leadForms.edit).
[leadForms.edit](https://dev.vk.com/method/leadForms.edit).
Полученные данные можно использовать в методах
[prettyCards.create](https://vk.com/dev/prettyCards.create)
[prettyCards.create](https://dev.vk.com/method/prettyCards.create)
и
[prettyCards.edit](https://vk.com/dev/prettyCards.edit).
[prettyCards.edit](https://dev.vk.com/method/prettyCards.edit).
### Загрузки фотографии в коллекцию приложения для виджетов приложений сообществ

View File

@ -6,7 +6,7 @@ import (
// AccountBan account.ban.
//
// https://vk.com/dev/account.ban
// https://dev.vk.com/method/account.ban
func (vk *VK) AccountBan(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.ban", &response, params)
return
@ -19,7 +19,7 @@ type AccountChangePasswordResponse struct {
// AccountChangePassword changes a user password after access is successfully restored with the auth.restore method.
//
// https://vk.com/dev/account.changePassword
// https://dev.vk.com/method/account.changePassword
func (vk *VK) AccountChangePassword(params Params) (response AccountChangePasswordResponse, err error) {
err = vk.RequestUnmarshal("account.changePassword", &response, params)
return
@ -35,7 +35,7 @@ type AccountGetActiveOffersResponse struct {
// If the user fulfill their conditions, he will be able to get
// the appropriate number of votes to his balance.
//
// https://vk.com/dev/account.getActiveOffers
// https://dev.vk.com/method/account.getActiveOffers
func (vk *VK) AccountGetActiveOffers(params Params) (response AccountGetActiveOffersResponse, err error) {
err = vk.RequestUnmarshal("account.getActiveOffers", &response, params)
return
@ -43,7 +43,7 @@ func (vk *VK) AccountGetActiveOffers(params Params) (response AccountGetActiveOf
// AccountGetAppPermissions gets settings of the user in this application.
//
// https://vk.com/dev/account.getAppPermissions
// https://dev.vk.com/method/account.getAppPermissions
func (vk *VK) AccountGetAppPermissions(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.getAppPermissions", &response, params)
return
@ -58,7 +58,7 @@ type AccountGetBannedResponse struct {
// AccountGetBanned returns a user's blacklist.
//
// https://vk.com/dev/account.getBanned
// https://dev.vk.com/method/account.getBanned
func (vk *VK) AccountGetBanned(params Params) (response AccountGetBannedResponse, err error) {
err = vk.RequestUnmarshal("account.getBanned", &response, params)
return
@ -69,7 +69,7 @@ type AccountGetCountersResponse object.AccountAccountCounters
// AccountGetCounters returns non-null values of user counters.
//
// https://vk.com/dev/account.getCounters
// https://dev.vk.com/method/account.getCounters
func (vk *VK) AccountGetCounters(params Params) (response AccountGetCountersResponse, err error) {
err = vk.RequestUnmarshal("account.getCounters", &response, params)
return
@ -80,7 +80,7 @@ type AccountGetInfoResponse object.AccountInfo
// AccountGetInfo returns current account info.
//
// https://vk.com/dev/account.getInfo
// https://dev.vk.com/method/account.getInfo
func (vk *VK) AccountGetInfo(params Params) (response AccountGetInfoResponse, err error) {
err = vk.RequestUnmarshal("account.getInfo", &response, params)
return
@ -91,7 +91,7 @@ type AccountGetProfileInfoResponse object.AccountUserSettings
// AccountGetProfileInfo returns the current account info.
//
// https://vk.com/dev/account.getProfileInfo
// https://dev.vk.com/method/account.getProfileInfo
func (vk *VK) AccountGetProfileInfo(params Params) (response AccountGetProfileInfoResponse, err error) {
err = vk.RequestUnmarshal("account.getProfileInfo", &response, params)
return
@ -102,7 +102,7 @@ type AccountGetPushSettingsResponse object.AccountPushSettings
// AccountGetPushSettings account.getPushSettings Gets settings of push notifications.
//
// https://vk.com/dev/account.getPushSettings
// https://dev.vk.com/method/account.getPushSettings
func (vk *VK) AccountGetPushSettings(params Params) (response AccountGetPushSettingsResponse, err error) {
err = vk.RequestUnmarshal("account.getPushSettings", &response, params)
return
@ -110,7 +110,7 @@ func (vk *VK) AccountGetPushSettings(params Params) (response AccountGetPushSett
// AccountRegisterDevice subscribes an iOS/Android/Windows/Mac based device to receive push notifications.
//
// https://vk.com/dev/account.registerDevice
// https://dev.vk.com/method/account.registerDevice
func (vk *VK) AccountRegisterDevice(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.registerDevice", &response, params)
return
@ -124,7 +124,7 @@ type AccountSaveProfileInfoResponse struct {
// AccountSaveProfileInfo edits current profile info.
//
// https://vk.com/dev/account.saveProfileInfo
// https://dev.vk.com/method/account.saveProfileInfo
func (vk *VK) AccountSaveProfileInfo(params Params) (response AccountSaveProfileInfoResponse, err error) {
err = vk.RequestUnmarshal("account.saveProfileInfo", &response, params)
return
@ -132,7 +132,7 @@ func (vk *VK) AccountSaveProfileInfo(params Params) (response AccountSaveProfile
// AccountSetInfo allows to edit the current account info.
//
// https://vk.com/dev/account.setInfo
// https://dev.vk.com/method/account.setInfo
func (vk *VK) AccountSetInfo(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.setInfo", &response, params)
return
@ -143,7 +143,7 @@ func (vk *VK) AccountSetInfo(params Params) (response int, err error) {
//
// Deprecated: This method is deprecated and may be disabled soon, please avoid
//
// https://vk.com/dev/account.setNameInMenu
// https://dev.vk.com/method/account.setNameInMenu
func (vk *VK) AccountSetNameInMenu(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.setNameInMenu", &response, params)
return
@ -151,7 +151,7 @@ func (vk *VK) AccountSetNameInMenu(params Params) (response int, err error) {
// AccountSetOffline marks a current user as offline.
//
// https://vk.com/dev/account.setOffline
// https://dev.vk.com/method/account.setOffline
func (vk *VK) AccountSetOffline(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.setOffline", &response, params)
return
@ -159,7 +159,7 @@ func (vk *VK) AccountSetOffline(params Params) (response int, err error) {
// AccountSetOnline marks the current user as online for 5 minutes.
//
// https://vk.com/dev/account.setOnline
// https://dev.vk.com/method/account.setOnline
func (vk *VK) AccountSetOnline(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.setOnline", &response, params)
return
@ -167,7 +167,7 @@ func (vk *VK) AccountSetOnline(params Params) (response int, err error) {
// AccountSetPushSettings change push settings.
//
// https://vk.com/dev/account.setPushSettings
// https://dev.vk.com/method/account.setPushSettings
func (vk *VK) AccountSetPushSettings(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.setPushSettings", &response, params)
return
@ -175,7 +175,7 @@ func (vk *VK) AccountSetPushSettings(params Params) (response int, err error) {
// AccountSetSilenceMode mutes push notifications for the set period of time.
//
// https://vk.com/dev/account.setSilenceMode
// https://dev.vk.com/method/account.setSilenceMode
func (vk *VK) AccountSetSilenceMode(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.setSilenceMode", &response, params)
return
@ -183,7 +183,7 @@ func (vk *VK) AccountSetSilenceMode(params Params) (response int, err error) {
// AccountUnban account.unban.
//
// https://vk.com/dev/account.unban
// https://dev.vk.com/method/account.unban
func (vk *VK) AccountUnban(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.unban", &response, params)
return
@ -191,7 +191,7 @@ func (vk *VK) AccountUnban(params Params) (response int, err error) {
// AccountUnregisterDevice unsubscribes a device from push notifications.
//
// https://vk.com/dev/account.unregisterDevice
// https://dev.vk.com/method/account.unregisterDevice
func (vk *VK) AccountUnregisterDevice(params Params) (response int, err error) {
err = vk.RequestUnmarshal("account.unregisterDevice", &response, params)
return

View File

@ -45,7 +45,7 @@ type AdsAddOfficeUsersResponse []AdsAddOfficeUsersItem
// AdsAddOfficeUsers adds managers and/or supervisors to advertising account.
//
// https://vk.com/dev/ads.addOfficeUsers
// https://dev.vk.com/method/ads.addOfficeUsers
func (vk *VK) AdsAddOfficeUsers(params Params) (response AdsAddOfficeUsersResponse, err error) {
err = vk.RequestUnmarshal("ads.addOfficeUsers", &response, params)
return
@ -65,7 +65,7 @@ type AdsCheckLinkResponse struct {
// AdsCheckLink allows to check the ad link.
//
// https://vk.com/dev/ads.checkLink
// https://dev.vk.com/method/ads.checkLink
func (vk *VK) AdsCheckLink(params Params) (response AdsCheckLinkResponse, err error) {
err = vk.RequestUnmarshal("ads.checkLink", &response, params)
return
@ -82,7 +82,7 @@ type AdsCreateAdsResponse []struct {
// Please note! Maximum allowed number of ads created in one request is 5.
// Minimum size of ad audience is 50 people.
//
// https://vk.com/dev/ads.createAds
// https://dev.vk.com/method/ads.createAds
func (vk *VK) AdsCreateAds(params Params) (response AdsCreateAdsResponse, err error) {
err = vk.RequestUnmarshal("ads.createAds", &response, params)
return
@ -98,7 +98,7 @@ type AdsCreateCampaignsResponse []struct {
//
// Please note! Allowed number of campaigns created in one request is 50.
//
// https://vk.com/dev/ads.createCampaigns
// https://dev.vk.com/method/ads.createCampaigns
func (vk *VK) AdsCreateCampaigns(params Params) (response AdsCreateCampaignsResponse, err error) {
err = vk.RequestUnmarshal("ads.createCampaigns", &response, params)
return
@ -116,7 +116,7 @@ type AdsCreateClientsResponse []struct {
//
// Please note! Allowed number of clients created in one request is 50.
//
// https://vk.com/dev/ads.createClients
// https://dev.vk.com/method/ads.createClients
func (vk *VK) AdsCreateClients(params Params) (response AdsCreateClientsResponse, err error) {
err = vk.RequestUnmarshal("ads.createClients", &response, params)
return
@ -129,7 +129,7 @@ type AdsCreateLookalikeRequestResponse struct {
// AdsCreateLookalikeRequest creates a request to find a similar audience.
//
// https://vk.com/dev/ads.createLookalikeRequest
// https://dev.vk.com/method/ads.createLookalikeRequest
func (vk *VK) AdsCreateLookalikeRequest(params Params) (response AdsCreateLookalikeRequestResponse, err error) {
err = vk.RequestUnmarshal("ads.createLookalikeRequest", &response, params)
return
@ -154,7 +154,7 @@ type AdsCreateTargetGroupResponse struct {
// Please note! Maximum allowed number of groups for one advertising
// account is 100.
//
// https://vk.com/dev/ads.createTargetGroup
// https://dev.vk.com/method/ads.createTargetGroup
func (vk *VK) AdsCreateTargetGroup(params Params) (response AdsCreateTargetGroupResponse, err error) {
err = vk.RequestUnmarshal("ads.createTargetGroup", &response, params)
return
@ -175,7 +175,7 @@ type AdsCreateTargetPixelResponse struct {
//
// Maximum pixels number per advertising account is 25.
//
// https://vk.com/dev/ads.createTargetPixel
// https://dev.vk.com/method/ads.createTargetPixel
func (vk *VK) AdsCreateTargetPixel(params Params) (response AdsCreateTargetPixelResponse, err error) {
err = vk.RequestUnmarshal("ads.createTargetPixel", &response, params)
return
@ -190,7 +190,7 @@ type AdsDeleteAdsResponse []ErrorType
//
// Warning! Maximum allowed number of ads archived in one request is 100.
//
// https://vk.com/dev/ads.deleteAds
// https://dev.vk.com/method/ads.deleteAds
func (vk *VK) AdsDeleteAds(params Params) (response AdsDeleteAdsResponse, err error) {
err = vk.RequestUnmarshal("ads.deleteAds", &response, params)
return
@ -203,10 +203,9 @@ type AdsDeleteCampaignsResponse []ErrorType
// AdsDeleteCampaigns archives advertising campaigns.
//
//
// Warning! Maximum allowed number of campaigns archived in one request is 100.
//
// https://vk.com/dev/ads.deleteCampaigns
// https://dev.vk.com/method/ads.deleteCampaigns
func (vk *VK) AdsDeleteCampaigns(params Params) (response AdsDeleteCampaignsResponse, err error) {
err = vk.RequestUnmarshal("ads.deleteCampaigns", &response, params)
return
@ -223,7 +222,7 @@ type AdsDeleteClientsResponse []ErrorType
//
// Please note! Maximum allowed number of clients edited in one request is 10.
//
// https://vk.com/dev/ads.deleteClients
// https://dev.vk.com/method/ads.deleteClients
func (vk *VK) AdsDeleteClients(params Params) (response AdsDeleteClientsResponse, err error) {
err = vk.RequestUnmarshal("ads.deleteClients", &response, params)
return
@ -231,7 +230,7 @@ func (vk *VK) AdsDeleteClients(params Params) (response AdsDeleteClientsResponse
// AdsDeleteTargetGroup deletes target group.
//
// https://vk.com/dev/ads.deleteTargetGroup
// https://dev.vk.com/method/ads.deleteTargetGroup
func (vk *VK) AdsDeleteTargetGroup(params Params) (response int, err error) {
err = vk.RequestUnmarshal("ads.deleteTargetGroup", &response, params)
return
@ -239,7 +238,7 @@ func (vk *VK) AdsDeleteTargetGroup(params Params) (response int, err error) {
// AdsDeleteTargetPixel deletes target pixel.
//
// https://vk.com/dev/ads.deleteTargetPixel
// https://dev.vk.com/method/ads.deleteTargetPixel
func (vk *VK) AdsDeleteTargetPixel(params Params) (response int, err error) {
err = vk.RequestUnmarshal("ads.deleteTargetPixel", &response, params)
return
@ -250,7 +249,7 @@ type AdsGetAccountsResponse []object.AdsAccount
// AdsGetAccounts returns a list of advertising accounts.
//
// https://vk.com/dev/ads.getAccounts
// https://dev.vk.com/method/ads.getAccounts
func (vk *VK) AdsGetAccounts(params Params) (response AdsGetAccountsResponse, err error) {
err = vk.RequestUnmarshal("ads.getAccounts", &response, params)
return
@ -261,7 +260,7 @@ type AdsGetAdsResponse []object.AdsAd
// AdsGetAds returns a list of ads.
//
// https://vk.com/dev/ads.getAds
// https://dev.vk.com/method/ads.getAds
func (vk *VK) AdsGetAds(params Params) (response AdsGetAdsResponse, err error) {
err = vk.RequestUnmarshal("ads.getAds", &response, params)
return
@ -272,7 +271,7 @@ type AdsGetAdsLayoutResponse []object.AdsAdLayout
// AdsGetAdsLayout returns descriptions of ad layouts.
//
// https://vk.com/dev/ads.getAdsLayout
// https://dev.vk.com/method/ads.getAdsLayout
func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse, err error) {
err = vk.RequestUnmarshal("ads.getAdsLayout", &response, params)
return
@ -283,7 +282,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetAdsTargeting ...
//
// https://vk.com/dev/ads.getAdsTargeting
// https://dev.vk.com/method/ads.getAdsTargeting
// func (vk *VK) AdsGetAdsTargeting(params Params) (response AdsGetAdsTargetingResponse, err error) {
// err = vk.RequestUnmarshal("ads.getAdsTargeting", &response, params)
// return
@ -294,7 +293,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetBudget ...
//
// https://vk.com/dev/ads.getBudget
// https://dev.vk.com/method/ads.getBudget
// func (vk *VK) AdsGetBudget(params Params) (response AdsGetBudgetResponse, err error) {
// err = vk.RequestUnmarshal("ads.getBudget", &response, params)
// return
@ -305,7 +304,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetCampaigns ...
//
// https://vk.com/dev/ads.getCampaigns
// https://dev.vk.com/method/ads.getCampaigns
// func (vk *VK) AdsGetCampaigns(params Params) (response AdsGetCampaignsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getCampaigns", &response, params)
// return
@ -316,7 +315,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetCategories ...
//
// https://vk.com/dev/ads.getCategories
// https://dev.vk.com/method/ads.getCategories
// func (vk *VK) AdsGetCategories(params Params) (response AdsGetCategoriesResponse, err error) {
// err = vk.RequestUnmarshal("ads.getCategories", &response, params)
// return
@ -327,7 +326,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetClients ...
//
// https://vk.com/dev/ads.getClients
// https://dev.vk.com/method/ads.getClients
// func (vk *VK) AdsGetClients(params Params) (response AdsGetClientsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getClients", &response, params)
// return
@ -338,7 +337,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetDemographics ...
//
// https://vk.com/dev/ads.getDemographics
// https://dev.vk.com/method/ads.getDemographics
// func (vk *VK) AdsGetDemographics(params Params) (response AdsGetDemographicsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getDemographics", &response, params)
// return
@ -349,7 +348,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetFloodStats ...
//
// https://vk.com/dev/ads.getFloodStats
// https://dev.vk.com/method/ads.getFloodStats
// func (vk *VK) AdsGetFloodStats(params Params) (response AdsGetFloodStatsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getFloodStats", &response, params)
// return
@ -360,7 +359,7 @@ func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse,
// TODO: AdsGetLookalikeRequests ...
//
// https://vk.com/dev/ads.getLookalikeRequests
// https://dev.vk.com/method/ads.getLookalikeRequests
// func (vk *VK) AdsGetLookalikeRequests(params Params) (response AdsGetLookalikeRequestsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getLookalikeRequests", &response, params)
// return
@ -373,7 +372,7 @@ type AdsGetMusiciansResponse struct {
// AdsGetMusicians returns a list of musicians.
//
// https://vk.com/dev/ads.getMusicians
// https://dev.vk.com/method/ads.getMusicians
func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse, err error) {
err = vk.RequestUnmarshal("ads.getMusicians", &response, params)
return
@ -384,7 +383,7 @@ func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse,
// TODO: AdsGetOfficeUsers ...
//
// https://vk.com/dev/ads.getOfficeUsers
// https://dev.vk.com/method/ads.getOfficeUsers
// func (vk *VK) AdsGetOfficeUsers(params Params) (response AdsGetOfficeUsersResponse, err error) {
// err = vk.RequestUnmarshal("ads.getOfficeUsers", &response, params)
// return
@ -395,7 +394,7 @@ func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse,
// TODO: AdsGetPostsReach ...
//
// https://vk.com/dev/ads.getPostsReach
// https://dev.vk.com/method/ads.getPostsReach
// func (vk *VK) AdsGetPostsReach(params Params) (response AdsGetPostsReachResponse, err error) {
// err = vk.RequestUnmarshal("ads.getPostsReach", &response, params)
// return
@ -406,7 +405,7 @@ func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse,
// TODO: AdsGetRejectionReason ...
//
// https://vk.com/dev/ads.getRejectionReason
// https://dev.vk.com/method/ads.getRejectionReason
// func (vk *VK) AdsGetRejectionReason(params Params) (response AdsGetRejectionReasonResponse, err error) {
// err = vk.RequestUnmarshal("ads.getRejectionReason", &response, params)
// return
@ -417,7 +416,7 @@ func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse,
// TODO: AdsGetStatistics ...
//
// https://vk.com/dev/ads.getStatistics
// https://dev.vk.com/method/ads.getStatistics
// func (vk *VK) AdsGetStatistics(params Params) (response AdsGetStatisticsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getStatistics", &response, params)
// return
@ -428,7 +427,7 @@ func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse,
// TODO: AdsGetSuggestions ...
//
// https://vk.com/dev/ads.getSuggestions
// https://dev.vk.com/method/ads.getSuggestions
// func (vk *VK) AdsGetSuggestions(params Params) (response AdsGetSuggestionsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getSuggestions", &response, params)
// return
@ -439,7 +438,7 @@ type AdsGetTargetGroupsResponse []object.AdsTargetGroup
// AdsGetTargetGroups returns a list of target groups.
//
// https://vk.com/dev/ads.getTargetGroups
// https://dev.vk.com/method/ads.getTargetGroups
func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResponse, err error) {
err = vk.RequestUnmarshal("ads.getTargetGroups", &response, params)
return
@ -450,7 +449,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
// TODO: AdsGetTargetPixels ...
//
// https://vk.com/dev/ads.getTargetPixels
// https://dev.vk.com/method/ads.getTargetPixels
// func (vk *VK) AdsGetTargetPixels(params Params) (response AdsGetTargetPixelsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getTargetPixels", &response, params)
// return
@ -461,7 +460,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
// TODO: AdsGetTargetingStats ...
//
// https://vk.com/dev/ads.getTargetingStats
// https://dev.vk.com/method/ads.getTargetingStats
// func (vk *VK) AdsGetTargetingStats(params Params) (response AdsGetTargetingStatsResponse, err error) {
// err = vk.RequestUnmarshal("ads.getTargetingStats", &response, params)
// return
@ -472,7 +471,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
// TODO: AdsGetUploadURL ...
//
// https://vk.com/dev/ads.getUploadURL
// https://dev.vk.com/method/ads.getUploadURL
// func (vk *VK) AdsGetUploadURL(params Params) (response AdsGetUploadURLResponse, err error) {
// err = vk.RequestUnmarshal("ads.getUploadURL", &response, params)
// return
@ -483,7 +482,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
// TODO: AdsGetVideoUploadURL ...
//
// https://vk.com/dev/ads.getVideoUploadURL
// https://dev.vk.com/method/ads.getVideoUploadURL
// func (vk *VK) AdsGetVideoUploadURL(params Params) (response AdsGetVideoUploadURLResponse, err error) {
// err = vk.RequestUnmarshal("ads.getVideoUploadURL", &response, params)
// return
@ -494,7 +493,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
// TODO: AdsImportTargetContacts ...
//
// https://vk.com/dev/ads.importTargetContacts
// https://dev.vk.com/method/ads.importTargetContacts
// func (vk *VK) AdsImportTargetContacts(params Params) (response AdsImportTargetContactsResponse, err error) {
// err = vk.RequestUnmarshal("ads.importTargetContacts", &response, params)
// return
@ -505,7 +504,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
// TODO: AdsRemoveOfficeUsers ...
//
// https://vk.com/dev/ads.removeOfficeUsers
// https://dev.vk.com/method/ads.removeOfficeUsers
// func (vk *VK) AdsRemoveOfficeUsers(params Params) (response AdsRemoveOfficeUsersResponse, err error) {
// err = vk.RequestUnmarshal("ads.removeOfficeUsers", &response, params)
// return
@ -519,7 +518,7 @@ func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResp
//
// Contacts are excluded within a few hours of the request.
//
// https://vk.com/dev/ads.removeTargetContacts
// https://dev.vk.com/method/ads.removeTargetContacts
func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
err = vk.RequestUnmarshal("ads.removeTargetContacts", &response, params)
return
@ -530,7 +529,7 @@ func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
// TODO: AdsSaveLookalikeRequestResult ...
//
// https://vk.com/dev/ads.saveLookalikeRequestResult
// https://dev.vk.com/method/ads.saveLookalikeRequestResult
// func (vk *VK) AdsSaveLookalikeRequestResult(params Params) (
// response AdsSaveLookalikeRequestResultResponse,
// err error,
@ -544,7 +543,7 @@ func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
// TODO: AdsShareTargetGroup ...
//
// https://vk.com/dev/ads.shareTargetGroup
// https://dev.vk.com/method/ads.shareTargetGroup
// func (vk *VK) AdsShareTargetGroup(params Params) (response AdsShareTargetGroupResponse, err error) {
// err = vk.RequestUnmarshal("ads.shareTargetGroup", &response, params)
// return
@ -555,7 +554,7 @@ func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
// TODO: AdsUpdateAds ...
//
// https://vk.com/dev/ads.updateAds
// https://dev.vk.com/method/ads.updateAds
// func (vk *VK) AdsUpdateAds(params Params) (response AdsUpdateAdsResponse, err error) {
// err = vk.RequestUnmarshal("ads.updateAds", &response, params)
// return
@ -566,7 +565,7 @@ func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
// TODO: AdsUpdateCampaigns ...
//
// https://vk.com/dev/ads.updateCampaigns
// https://dev.vk.com/method/ads.updateCampaigns
// func (vk *VK) AdsUpdateCampaigns(params Params) (response AdsUpdateCampaignsResponse, err error) {
// err = vk.RequestUnmarshal("ads.updateCampaigns", &response, params)
// return
@ -577,7 +576,7 @@ func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
// TODO: AdsUpdateClients ...
//
// https://vk.com/dev/ads.updateClients
// https://dev.vk.com/method/ads.updateClients
// func (vk *VK) AdsUpdateClients(params Params) (response AdsUpdateClientsResponse, err error) {
// err = vk.RequestUnmarshal("ads.updateClients", &response, params)
// return
@ -585,7 +584,7 @@ func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) {
// AdsUpdateTargetGroup edits target group.
//
// https://vk.com/dev/ads.updateTargetGroup
// https://dev.vk.com/method/ads.updateTargetGroup
func (vk *VK) AdsUpdateTargetGroup(params Params) (response int, err error) {
err = vk.RequestUnmarshal("ads.updateTargetGroup", &response, params)
return
@ -593,7 +592,7 @@ func (vk *VK) AdsUpdateTargetGroup(params Params) (response int, err error) {
// AdsUpdateTargetPixel edits target pixel.
//
// https://vk.com/dev/ads.updateTargetPixel
// https://dev.vk.com/method/ads.updateTargetPixel
func (vk *VK) AdsUpdateTargetPixel(params Params) (response int, err error) {
err = vk.RequestUnmarshal("ads. updateTargetPixel", &response, params)
return

View File

@ -1,7 +1,7 @@
/*
Package api implements VK API.
See more https://vk.com/dev/api_requests
See more https://dev.vk.com/ru/api/api-requests
*/
package api // import "github.com/SevereCloud/vksdk/v2/api"
@ -46,7 +46,7 @@ const (
// 1 000 000+ 35 requests.
//
// The ads section methods are subject to their own limitations,
// you can read them on this page - https://vk.com/dev/ads_limits
// you can read them on this page - https://dev.vk.com/ru/method/ads
//
// If one of this limits is exceeded, the server will return following error:
// "Too many requests per second". (errors.TooMany).
@ -58,7 +58,7 @@ const (
// quantitative restrictions on calling the same type of methods.
//
// After exceeding the quantitative limit, access to a particular method may
// require entering a captcha (see https://vk.com/dev/captcha_error),
// require entering a captcha (see https://dev.vk.com/ru/api/captcha-error),
// and may also be temporarily restricted (in this case, the server does
// not return a response to the call of a particular method, but handles
// any other requests without problems).
@ -78,7 +78,7 @@ const (
//
// captcha_key - text entered by the user.
//
// More info: https://vk.com/dev/api_requests
// More info: https://dev.vk.com/ru/api/api-requests
const (
LimitUserToken = 3
LimitGroupToken = 20
@ -152,7 +152,7 @@ type Params map[string]interface{}
// cyrillic symbols will be transliterated automatically.
// Numeric format from account.getInfo is supported as well.
//
// p.Lang(object.LangRU)
// p.Lang(object.LangRU)
//
// See all language code in module object.
func (p Params) Lang(v int) Params {
@ -169,7 +169,7 @@ func (p Params) TestMode(v bool) Params {
// CaptchaSID received ID.
//
// See https://vk.com/dev/captcha_error
// See https://dev.vk.com/ru/api/captcha-error
func (p Params) CaptchaSID(v string) Params {
p["captcha_sid"] = v
return p
@ -177,7 +177,7 @@ func (p Params) CaptchaSID(v string) Params {
// CaptchaKey text input.
//
// See https://vk.com/dev/captcha_error
// See https://dev.vk.com/ru/api/captcha-error
func (p Params) CaptchaKey(v string) Params {
p["captcha_key"] = v
return p
@ -185,7 +185,7 @@ func (p Params) CaptchaKey(v string) Params {
// Confirm parameter.
//
// See https://vk.com/dev/need_confirmation
// See https://dev.vk.com/ru/api/confirmation-required-error
func (p Params) Confirm(v bool) Params {
p["confirm"] = v
return p
@ -203,10 +203,13 @@ func buildQuery(sliceParams ...Params) (context.Context, url.Values) {
for _, params := range sliceParams {
for key, value := range params {
if key != ":context" {
query.Set(key, FmtValue(value, 0))
} else {
switch key {
case "access_token":
continue
case ":context":
ctx = value.(context.Context)
default:
query.Set(key, FmtValue(value, 0))
}
}
}
@ -238,6 +241,7 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er
vk.lastTime = time.Now()
vk.rps = 0
}
vk.rps++
vk.mux.Unlock()
@ -245,7 +249,7 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er
rawBody := bytes.NewBufferString(query.Encode())
req, err := http.NewRequestWithContext(ctx, "POST", u, rawBody)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, rawBody)
if err != nil {
return response, err
}
@ -255,6 +259,9 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er
acceptEncoding = "zstd"
}
token := sliceParams[len(sliceParams)-1]["access_token"].(string)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("User-Agent", vk.UserAgent)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

View File

@ -4,9 +4,17 @@ import (
"github.com/SevereCloud/vksdk/v2/object"
)
// AppsAddUsersToTestingGroup method.
//
// https://dev.vk.com/method/apps.addUsersToTestingGroup
func (vk *VK) AppsAddUsersToTestingGroup(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.addUsersToTestingGroup", &response, params)
return
}
// AppsDeleteAppRequests deletes all request notifications from the current app.
//
// https://vk.com/dev/apps.deleteAppRequests
// https://dev.vk.com/method/apps.deleteAppRequests
func (vk *VK) AppsDeleteAppRequests(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.deleteAppRequests", &response, params)
return
@ -21,7 +29,7 @@ type AppsGetResponse struct {
// AppsGet returns applications data.
//
// https://vk.com/dev/apps.get
// https://dev.vk.com/method/apps.get
func (vk *VK) AppsGet(params Params) (response AppsGetResponse, err error) {
err = vk.RequestUnmarshal("apps.get", &response, params)
return
@ -36,7 +44,7 @@ type AppsGetCatalogResponse struct {
// AppsGetCatalog returns a list of applications (apps) available to users in the App Catalog.
//
// https://vk.com/dev/apps.getCatalog
// https://dev.vk.com/method/apps.getCatalog
func (vk *VK) AppsGetCatalog(params Params) (response AppsGetCatalogResponse, err error) {
err = vk.RequestUnmarshal("apps.getCatalog", &response, params)
return
@ -50,9 +58,9 @@ type AppsGetFriendsListResponse struct {
// AppsGetFriendsList creates friends list for requests and invites in current app.
//
// extended=0
// extended=0
//
// https://vk.com/dev/apps.getFriendsList
// https://dev.vk.com/method/apps.getFriendsList
func (vk *VK) AppsGetFriendsList(params Params) (response AppsGetFriendsListResponse, err error) {
err = vk.RequestUnmarshal("apps.getFriendsList", &response, params, Params{"extended": false})
@ -67,9 +75,9 @@ type AppsGetFriendsListExtendedResponse struct {
// AppsGetFriendsListExtended creates friends list for requests and invites in current app.
//
// extended=1
// extended=1
//
// https://vk.com/dev/apps.getFriendsList
// https://dev.vk.com/method/apps.getFriendsList
func (vk *VK) AppsGetFriendsListExtended(params Params) (response AppsGetFriendsListExtendedResponse, err error) {
err = vk.RequestUnmarshal("apps.getFriendsList", &response, params, Params{"extended": true})
@ -84,9 +92,9 @@ type AppsGetLeaderboardResponse struct {
// AppsGetLeaderboard returns players rating in the game.
//
// extended=0
// extended=0
//
// https://vk.com/dev/apps.getLeaderboard
// https://dev.vk.com/method/apps.getLeaderboard
func (vk *VK) AppsGetLeaderboard(params Params) (response AppsGetLeaderboardResponse, err error) {
err = vk.RequestUnmarshal("apps.getLeaderboard", &response, params, Params{"extended": false})
@ -105,9 +113,9 @@ type AppsGetLeaderboardExtendedResponse struct {
// AppsGetLeaderboardExtended returns players rating in the game.
//
// extended=1
// extended=1
//
// https://vk.com/dev/apps.getLeaderboard
// https://dev.vk.com/method/apps.getLeaderboard
func (vk *VK) AppsGetLeaderboardExtended(params Params) (response AppsGetLeaderboardExtendedResponse, err error) {
err = vk.RequestUnmarshal("apps.getLeaderboard", &response, params, Params{"extended": true})
@ -124,7 +132,7 @@ type AppsGetScopesResponse struct {
//
// TODO: write docs.
//
// https://vk.com/dev/apps.getScopes
// https://dev.vk.com/method/apps.getScopes
func (vk *VK) AppsGetScopes(params Params) (response AppsGetScopesResponse, err error) {
err = vk.RequestUnmarshal("apps.getScopes", &response, params)
return
@ -134,16 +142,56 @@ func (vk *VK) AppsGetScopes(params Params) (response AppsGetScopesResponse, err
//
// NOTE: vk wtf!?
//
// https://vk.com/dev/apps.getScore
// https://dev.vk.com/method/apps.getScore
func (vk *VK) AppsGetScore(params Params) (response string, err error) {
err = vk.RequestUnmarshal("apps.getScore", &response, params)
return
}
// AppsGetTestingGroupsResponse struct.
type AppsGetTestingGroupsResponse []object.AppsTestingGroup
// AppsGetTestingGroups method.
//
// https://dev.vk.com/method/apps.getTestingGroups
func (vk *VK) AppsGetTestingGroups(params Params) (response AppsGetTestingGroupsResponse, err error) {
err = vk.RequestUnmarshal("apps.getTestingGroups", &response, params)
return
}
// AppsRemoveTestingGroup method.
//
// https://dev.vk.com/method/apps.removeTestingGroup
func (vk *VK) AppsRemoveTestingGroup(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.removeTestingGroup", &response, params)
return
}
// AppsRemoveUsersFromTestingGroups method.
//
// https://dev.vk.com/method/apps.removeUsersFromTestingGroups
func (vk *VK) AppsRemoveUsersFromTestingGroups(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.removeUsersFromTestingGroups", &response, params)
return
}
// AppsSendRequest sends a request to another user in an app that uses VK authorization.
//
// https://vk.com/dev/apps.sendRequest
// https://dev.vk.com/method/apps.sendRequest
func (vk *VK) AppsSendRequest(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.sendRequest", &response, params)
return
}
// AppsUpdateMetaForTestingGroupResponse struct.
type AppsUpdateMetaForTestingGroupResponse struct {
GroupID int `json:"group_id"`
}
// AppsUpdateMetaForTestingGroup method.
//
// https://dev.vk.com/method/apps.updateMetaForTestingGroup
func (vk *VK) AppsUpdateMetaForTestingGroup(params Params) (response AppsUpdateMetaForTestingGroupResponse, err error) {
err = vk.RequestUnmarshal("apps.updateMetaForTestingGroup", &response, params)
return
}

View File

@ -12,7 +12,7 @@ type AppWidgetsGetAppImageUploadServerResponse struct {
// AppWidgetsGetAppImageUploadServer returns a URL for uploading a
// photo to the app collection for community app widgets.
//
// https://vk.com/dev/appWidgets.getAppImageUploadServer
// https://dev.vk.com/method/appWidgets.getAppImageUploadServer
func (vk *VK) AppWidgetsGetAppImageUploadServer(params Params) (
response AppWidgetsGetAppImageUploadServerResponse,
err error,
@ -29,7 +29,7 @@ type AppWidgetsGetAppImagesResponse struct {
// AppWidgetsGetAppImages returns an app collection of images for community app widgets.
//
// https://vk.com/dev/appWidgets.getAppImages
// https://dev.vk.com/method/appWidgets.getAppImages
func (vk *VK) AppWidgetsGetAppImages(params Params) (response AppWidgetsGetAppImagesResponse, err error) {
err = vk.RequestUnmarshal("appWidgets.getAppImages", &response, params)
return
@ -43,7 +43,7 @@ type AppWidgetsGetGroupImageUploadServerResponse struct {
// AppWidgetsGetGroupImageUploadServer returns a URL for uploading
// a photo to the community collection for community app widgets.
//
// https://vk.com/dev/appWidgets.getGroupImageUploadServer
// https://dev.vk.com/method/appWidgets.getGroupImageUploadServer
func (vk *VK) AppWidgetsGetGroupImageUploadServer(params Params) (
response AppWidgetsGetGroupImageUploadServerResponse,
err error,
@ -60,7 +60,7 @@ type AppWidgetsGetGroupImagesResponse struct {
// AppWidgetsGetGroupImages returns a community collection of images for community app widgets.
//
// https://vk.com/dev/appWidgets.getGroupImages
// https://dev.vk.com/method/appWidgets.getGroupImages
func (vk *VK) AppWidgetsGetGroupImages(params Params) (response AppWidgetsGetGroupImagesResponse, err error) {
err = vk.RequestUnmarshal("appWidgets.getGroupImages", &response, params)
return
@ -68,7 +68,7 @@ func (vk *VK) AppWidgetsGetGroupImages(params Params) (response AppWidgetsGetGro
// AppWidgetsGetImagesByID returns an image for community app widgets by its ID.
//
// https://vk.com/dev/appWidgets.getImagesById
// https://dev.vk.com/method/appWidgets.getImagesById
func (vk *VK) AppWidgetsGetImagesByID(params Params) (response object.AppWidgetsImage, err error) {
err = vk.RequestUnmarshal("appWidgets.getImagesById", &response, params)
return
@ -76,7 +76,7 @@ func (vk *VK) AppWidgetsGetImagesByID(params Params) (response object.AppWidgets
// AppWidgetsSaveAppImage allows to save image into app collection for community app widgets.
//
// https://vk.com/dev/appWidgets.saveAppImage
// https://dev.vk.com/method/appWidgets.saveAppImage
func (vk *VK) AppWidgetsSaveAppImage(params Params) (response object.AppWidgetsImage, err error) {
err = vk.RequestUnmarshal("appWidgets.saveAppImage", &response, params)
return
@ -84,7 +84,7 @@ func (vk *VK) AppWidgetsSaveAppImage(params Params) (response object.AppWidgetsI
// AppWidgetsSaveGroupImage allows to save image into community collection for community app widgets.
//
// https://vk.com/dev/appWidgets.saveGroupImage
// https://dev.vk.com/method/appWidgets.saveGroupImage
func (vk *VK) AppWidgetsSaveGroupImage(params Params) (response object.AppWidgetsImage, err error) {
err = vk.RequestUnmarshal("appWidgets.saveGroupImage", &response, params)
return
@ -92,7 +92,7 @@ func (vk *VK) AppWidgetsSaveGroupImage(params Params) (response object.AppWidget
// AppWidgetsUpdate allows to update community app widget.
//
// https://vk.com/dev/appWidgets.update
// https://dev.vk.com/method/appWidgets.update
func (vk *VK) AppWidgetsUpdate(params Params) (response int, err error) {
err = vk.RequestUnmarshal("appWidgets.update", &response, params)

View File

@ -6,7 +6,7 @@ import (
// AuthCheckPhone checks a user's phone number for correctness.
//
// https://vk.com/dev/auth.checkPhone
// https://dev.vk.com/method/auth.checkPhone
//
// Deprecated: This method is deprecated and may be disabled soon, please avoid
// using it.
@ -23,7 +23,7 @@ type AuthRestoreResponse struct {
// AuthRestore allows to restore account access using a code received via SMS.
//
// https://vk.com/dev/auth.restore
// https://dev.vk.com/method/auth.restore
func (vk *VK) AuthRestore(params Params) (response AuthRestoreResponse, err error) {
err = vk.RequestUnmarshal("auth.restore", &response, params)
return
@ -45,20 +45,20 @@ func (vk *VK) AuthGetProfileInfoBySilentToken(params Params) (response AuthGetPr
// ExchangeSilentTokenSource call conditions exchangeSilentToken.
//
// 0 Unknown
// 1 Silent authentication
// 2 Auth by login and password
// 3 Extended registration
// 4 Auth by exchange token
// 5 Auth by exchange token on reset password
// 6 Auth by exchange token on unblock
// 7 Auth by exchange token on reset session
// 8 Auth by exchange token on change password
// 9 Finish phone validation on authentication
// 10 Auth by code
// 11 Auth by external oauth
// 12 Reactivation
// 15 Auth by SDK temporary access-token
// 0 Unknown
// 1 Silent authentication
// 2 Auth by login and password
// 3 Extended registration
// 4 Auth by exchange token
// 5 Auth by exchange token on reset password
// 6 Auth by exchange token on unblock
// 7 Auth by exchange token on reset session
// 8 Auth by exchange token on change password
// 9 Finish phone validation on authentication
// 10 Auth by code
// 11 Auth by external oauth
// 12 Reactivation
// 15 Auth by SDK temporary access-token
type ExchangeSilentTokenSource int
// AuthExchangeSilentAuthTokenResponse struct.

View File

@ -6,7 +6,7 @@ import (
// BoardAddTopic creates a new topic on a community's discussion board.
//
// https://vk.com/dev/board.addTopic
// https://dev.vk.com/method/board.addTopic
func (vk *VK) BoardAddTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.addTopic", &response, params)
return
@ -14,7 +14,7 @@ func (vk *VK) BoardAddTopic(params Params) (response int, err error) {
// BoardCloseTopic closes a topic on a community's discussion board so that comments cannot be posted.
//
// https://vk.com/dev/board.closeTopic
// https://dev.vk.com/method/board.closeTopic
func (vk *VK) BoardCloseTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.closeTopic", &response, params)
return
@ -22,7 +22,7 @@ func (vk *VK) BoardCloseTopic(params Params) (response int, err error) {
// BoardCreateComment adds a comment on a topic on a community's discussion board.
//
// https://vk.com/dev/board.createComment
// https://dev.vk.com/method/board.createComment
func (vk *VK) BoardCreateComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.createComment", &response, params)
return
@ -30,7 +30,7 @@ func (vk *VK) BoardCreateComment(params Params) (response int, err error) {
// BoardDeleteComment deletes a comment on a topic on a community's discussion board.
//
// https://vk.com/dev/board.deleteComment
// https://dev.vk.com/method/board.deleteComment
func (vk *VK) BoardDeleteComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.deleteComment", &response, params)
return
@ -38,7 +38,7 @@ func (vk *VK) BoardDeleteComment(params Params) (response int, err error) {
// BoardDeleteTopic deletes a topic from a community's discussion board.
//
// https://vk.com/dev/board.deleteTopic
// https://dev.vk.com/method/board.deleteTopic
func (vk *VK) BoardDeleteTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.deleteTopic", &response, params)
return
@ -46,7 +46,7 @@ func (vk *VK) BoardDeleteTopic(params Params) (response int, err error) {
// BoardEditComment edits a comment on a topic on a community's discussion board.
//
// https://vk.com/dev/board.editComment
// https://dev.vk.com/method/board.editComment
func (vk *VK) BoardEditComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.editComment", &response, params)
return
@ -54,7 +54,7 @@ func (vk *VK) BoardEditComment(params Params) (response int, err error) {
// BoardEditTopic edits the title of a topic on a community's discussion board.
//
// https://vk.com/dev/board.editTopic
// https://dev.vk.com/method/board.editTopic
func (vk *VK) BoardEditTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.editTopic", &response, params)
return
@ -62,7 +62,7 @@ func (vk *VK) BoardEditTopic(params Params) (response int, err error) {
// BoardFixTopic pins a topic (fixes its place) to the top of a community's discussion board.
//
// https://vk.com/dev/board.fixTopic
// https://dev.vk.com/method/board.fixTopic
func (vk *VK) BoardFixTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.fixTopic", &response, params)
return
@ -78,9 +78,9 @@ type BoardGetCommentsResponse struct {
// BoardGetComments returns a list of comments on a topic on a community's discussion board.
//
// extended=0
// extended=0
//
// https://vk.com/dev/board.getComments
// https://dev.vk.com/method/board.getComments
func (vk *VK) BoardGetComments(params Params) (response BoardGetCommentsResponse, err error) {
err = vk.RequestUnmarshal("board.getComments", &response, params, Params{"extended": false})
@ -99,9 +99,9 @@ type BoardGetCommentsExtendedResponse struct {
// BoardGetCommentsExtended returns a list of comments on a topic on a community's discussion board.
//
// extended=1
// extended=1
//
// https://vk.com/dev/board.getComments
// https://dev.vk.com/method/board.getComments
func (vk *VK) BoardGetCommentsExtended(params Params) (response BoardGetCommentsExtendedResponse, err error) {
err = vk.RequestUnmarshal("board.getComments", &response, params, Params{"extended": true})
@ -118,9 +118,9 @@ type BoardGetTopicsResponse struct {
// BoardGetTopics returns a list of topics on a community's discussion board.
//
// extended=0
// extended=0
//
// https://vk.com/dev/board.getTopics
// https://dev.vk.com/method/board.getTopics
func (vk *VK) BoardGetTopics(params Params) (response BoardGetTopicsResponse, err error) {
err = vk.RequestUnmarshal("board.getTopics", &response, params, Params{"extended": false})
@ -139,9 +139,9 @@ type BoardGetTopicsExtendedResponse struct {
// BoardGetTopicsExtended returns a list of topics on a community's discussion board.
//
// extended=1
// extended=1
//
// https://vk.com/dev/board.getTopics
// https://dev.vk.com/method/board.getTopics
func (vk *VK) BoardGetTopicsExtended(params Params) (response BoardGetTopicsExtendedResponse, err error) {
err = vk.RequestUnmarshal("board.getTopics", &response, params, Params{"extended": true})
@ -150,7 +150,7 @@ func (vk *VK) BoardGetTopicsExtended(params Params) (response BoardGetTopicsExte
// BoardOpenTopic re-opens a previously closed topic on a community's discussion board.
//
// https://vk.com/dev/board.openTopic
// https://dev.vk.com/method/board.openTopic
func (vk *VK) BoardOpenTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.openTopic", &response, params)
return
@ -158,7 +158,7 @@ func (vk *VK) BoardOpenTopic(params Params) (response int, err error) {
// BoardRestoreComment restores a comment deleted from a topic on a community's discussion board.
//
// https://vk.com/dev/board.restoreComment
// https://dev.vk.com/method/board.restoreComment
func (vk *VK) BoardRestoreComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.restoreComment", &response, params)
return
@ -166,7 +166,7 @@ func (vk *VK) BoardRestoreComment(params Params) (response int, err error) {
// BoardUnfixTopic unpins a pinned topic from the top of a community's discussion board.
//
// https://vk.com/dev/board.unfixTopic
// https://dev.vk.com/method/board.unfixTopic
func (vk *VK) BoardUnfixTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.unfixTopic", &response, params)
return

23
vendor/github.com/SevereCloud/vksdk/v2/api/calls.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
// CallsStartResponse struct.
type CallsStartResponse struct {
JoinLink string `json:"join_link"`
CallID string `json:"call_id"`
}
// CallsStart method.
//
// https://dev.vk.com/method/calls.start
func (vk *VK) CallsStart(params Params) (response CallsStartResponse, err error) {
err = vk.RequestUnmarshal("calls.start", &response, params)
return
}
// CallsForceFinish method.
//
// https://dev.vk.com/method/calls.forceFinish
func (vk *VK) CallsForceFinish(params Params) (response int, err error) {
err = vk.RequestUnmarshal("calls.forceFinish", &response, params)
return
}

View File

@ -12,7 +12,7 @@ type DatabaseGetChairsResponse struct {
// DatabaseGetChairs returns list of chairs on a specified faculty.
//
// https://vk.com/dev/database.getChairs
// https://dev.vk.com/method/database.getChairs
func (vk *VK) DatabaseGetChairs(params Params) (response DatabaseGetChairsResponse, err error) {
err = vk.RequestUnmarshal("database.getChairs", &response, params)
return
@ -26,7 +26,7 @@ type DatabaseGetCitiesResponse struct {
// DatabaseGetCities returns a list of cities.
//
// https://vk.com/dev/database.getCities
// https://dev.vk.com/method/database.getCities
func (vk *VK) DatabaseGetCities(params Params) (response DatabaseGetCitiesResponse, err error) {
err = vk.RequestUnmarshal("database.getCities", &response, params)
return
@ -37,7 +37,7 @@ type DatabaseGetCitiesByIDResponse []object.DatabaseCity
// DatabaseGetCitiesByID returns information about cities by their IDs.
//
// https://vk.com/dev/database.getCitiesByID
// https://dev.vk.com/method/database.getCitiesByID
func (vk *VK) DatabaseGetCitiesByID(params Params) (response DatabaseGetCitiesByIDResponse, err error) {
err = vk.RequestUnmarshal("database.getCitiesById", &response, params)
return
@ -51,7 +51,7 @@ type DatabaseGetCountriesResponse struct {
// DatabaseGetCountries returns a list of countries.
//
// https://vk.com/dev/database.getCountries
// https://dev.vk.com/method/database.getCountries
func (vk *VK) DatabaseGetCountries(params Params) (response DatabaseGetCountriesResponse, err error) {
err = vk.RequestUnmarshal("database.getCountries", &response, params)
return
@ -62,7 +62,7 @@ type DatabaseGetCountriesByIDResponse []object.BaseObject
// DatabaseGetCountriesByID returns information about countries by their IDs.
//
// https://vk.com/dev/database.getCountriesByID
// https://dev.vk.com/method/database.getCountriesByID
func (vk *VK) DatabaseGetCountriesByID(params Params) (response DatabaseGetCountriesByIDResponse, err error) {
err = vk.RequestUnmarshal("database.getCountriesById", &response, params)
return
@ -76,7 +76,7 @@ type DatabaseGetFacultiesResponse struct {
// DatabaseGetFaculties returns a list of faculties (i.e., university departments).
//
// https://vk.com/dev/database.getFaculties
// https://dev.vk.com/method/database.getFaculties
func (vk *VK) DatabaseGetFaculties(params Params) (response DatabaseGetFacultiesResponse, err error) {
err = vk.RequestUnmarshal("database.getFaculties", &response, params)
return
@ -90,7 +90,7 @@ type DatabaseGetMetroStationsResponse struct {
// DatabaseGetMetroStations returns the list of metro stations.
//
// https://vk.com/dev/database.getMetroStations
// https://dev.vk.com/method/database.getMetroStations
func (vk *VK) DatabaseGetMetroStations(params Params) (response DatabaseGetMetroStationsResponse, err error) {
err = vk.RequestUnmarshal("database.getMetroStations", &response, params)
return
@ -101,7 +101,7 @@ type DatabaseGetMetroStationsByIDResponse []object.DatabaseMetroStation
// DatabaseGetMetroStationsByID returns information about one or several metro stations by their identifiers.
//
// https://vk.com/dev/database.getMetroStationsById
// https://dev.vk.com/method/database.getMetroStationsById
func (vk *VK) DatabaseGetMetroStationsByID(params Params) (response DatabaseGetMetroStationsByIDResponse, err error) {
err = vk.RequestUnmarshal("database.getMetroStationsById", &response, params)
return
@ -115,7 +115,7 @@ type DatabaseGetRegionsResponse struct {
// DatabaseGetRegions returns a list of regions.
//
// https://vk.com/dev/database.getRegions
// https://dev.vk.com/method/database.getRegions
func (vk *VK) DatabaseGetRegions(params Params) (response DatabaseGetRegionsResponse, err error) {
err = vk.RequestUnmarshal("database.getRegions", &response, params)
return
@ -128,7 +128,7 @@ type DatabaseGetSchoolClassesResponse [][]interface{}
//
// BUG(VK): database.getSchoolClasses bad return.
//
// https://vk.com/dev/database.getSchoolClasses
// https://dev.vk.com/method/database.getSchoolClasses
func (vk *VK) DatabaseGetSchoolClasses(params Params) (response DatabaseGetSchoolClassesResponse, err error) {
err = vk.RequestUnmarshal("database.getSchoolClasses", &response, params)
return
@ -142,7 +142,7 @@ type DatabaseGetSchoolsResponse struct {
// DatabaseGetSchools returns a list of schools.
//
// https://vk.com/dev/database.getSchools
// https://dev.vk.com/method/database.getSchools
func (vk *VK) DatabaseGetSchools(params Params) (response DatabaseGetSchoolsResponse, err error) {
err = vk.RequestUnmarshal("database.getSchools", &response, params)
return
@ -156,7 +156,7 @@ type DatabaseGetUniversitiesResponse struct {
// DatabaseGetUniversities returns a list of higher education institutions.
//
// https://vk.com/dev/database.getUniversities
// https://dev.vk.com/method/database.getUniversities
func (vk *VK) DatabaseGetUniversities(params Params) (response DatabaseGetUniversitiesResponse, err error) {
err = vk.RequestUnmarshal("database.getUniversities", &response, params)
return

View File

@ -6,7 +6,7 @@ import (
// DocsAdd copies a document to a user's or community's document list.
//
// https://vk.com/dev/docs.add
// https://dev.vk.com/method/docs.add
func (vk *VK) DocsAdd(params Params) (response int, err error) {
err = vk.RequestUnmarshal("docs.add", &response, params)
return
@ -14,7 +14,7 @@ func (vk *VK) DocsAdd(params Params) (response int, err error) {
// DocsDelete deletes a user or community document.
//
// https://vk.com/dev/docs.delete
// https://dev.vk.com/method/docs.delete
func (vk *VK) DocsDelete(params Params) (response int, err error) {
err = vk.RequestUnmarshal("docs.delete", &response, params)
return
@ -22,7 +22,7 @@ func (vk *VK) DocsDelete(params Params) (response int, err error) {
// DocsEdit edits a document.
//
// https://vk.com/dev/docs.edit
// https://dev.vk.com/method/docs.edit
func (vk *VK) DocsEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("docs.edit", &response, params)
return
@ -36,7 +36,7 @@ type DocsGetResponse struct {
// DocsGet returns detailed information about user or community documents.
//
// https://vk.com/dev/docs.get
// https://dev.vk.com/method/docs.get
func (vk *VK) DocsGet(params Params) (response DocsGetResponse, err error) {
err = vk.RequestUnmarshal("docs.get", &response, params)
return
@ -47,7 +47,7 @@ type DocsGetByIDResponse []object.DocsDoc
// DocsGetByID returns information about documents by their IDs.
//
// https://vk.com/dev/docs.getById
// https://dev.vk.com/method/docs.getById
func (vk *VK) DocsGetByID(params Params) (response DocsGetByIDResponse, err error) {
err = vk.RequestUnmarshal("docs.getById", &response, params)
return
@ -60,7 +60,7 @@ type DocsGetMessagesUploadServerResponse struct {
// DocsGetMessagesUploadServer returns the server address for document upload.
//
// https://vk.com/dev/docs.getMessagesUploadServer
// https://dev.vk.com/method/docs.getMessagesUploadServer
func (vk *VK) DocsGetMessagesUploadServer(params Params) (response DocsGetMessagesUploadServerResponse, err error) {
err = vk.RequestUnmarshal("docs.getMessagesUploadServer", &response, params)
return
@ -74,7 +74,7 @@ type DocsGetTypesResponse struct {
// DocsGetTypes returns documents types available for current user.
//
// https://vk.com/dev/docs.getTypes
// https://dev.vk.com/method/docs.getTypes
func (vk *VK) DocsGetTypes(params Params) (response DocsGetTypesResponse, err error) {
err = vk.RequestUnmarshal("docs.getTypes", &response, params)
return
@ -87,7 +87,7 @@ type DocsGetUploadServerResponse struct {
// DocsGetUploadServer returns the server address for document upload.
//
// https://vk.com/dev/docs.getUploadServer
// https://dev.vk.com/method/docs.getUploadServer
func (vk *VK) DocsGetUploadServer(params Params) (response DocsGetUploadServerResponse, err error) {
err = vk.RequestUnmarshal("docs.getUploadServer", &response, params)
return
@ -100,7 +100,7 @@ type DocsGetWallUploadServerResponse struct {
// DocsGetWallUploadServer returns the server address for document upload onto a user's or community's wall.
//
// https://vk.com/dev/docs.getWallUploadServer
// https://dev.vk.com/method/docs.getWallUploadServer
func (vk *VK) DocsGetWallUploadServer(params Params) (response DocsGetWallUploadServerResponse, err error) {
err = vk.RequestUnmarshal("docs.getWallUploadServer", &response, params)
return
@ -116,7 +116,7 @@ type DocsSaveResponse struct {
// DocsSave saves a document after uploading it to a server.
//
// https://vk.com/dev/docs.save
// https://dev.vk.com/method/docs.save
func (vk *VK) DocsSave(params Params) (response DocsSaveResponse, err error) {
err = vk.RequestUnmarshal("docs.save", &response, params)
return
@ -130,7 +130,7 @@ type DocsSearchResponse struct {
// DocsSearch returns a list of documents matching the search criteria.
//
// https://vk.com/dev/docs.search
// https://dev.vk.com/method/docs.search
func (vk *VK) DocsSearch(params Params) (response DocsSearchResponse, err error) {
err = vk.RequestUnmarshal("docs.search", &response, params)
return

View File

@ -10,7 +10,7 @@ type DonutGetFriendsResponse struct {
// DonutGetFriends method.
//
// https://vk.com/dev/donut.getFriends
// https://dev.vk.com/method/donut.getFriends
func (vk *VK) DonutGetFriends(params Params) (response DonutGetFriendsResponse, err error) {
err = vk.RequestUnmarshal("donut.getFriends", &response, params)
return
@ -18,7 +18,7 @@ func (vk *VK) DonutGetFriends(params Params) (response DonutGetFriendsResponse,
// DonutGetSubscription method.
//
// https://vk.com/dev/donut.getSubscription
// https://dev.vk.com/method/donut.getSubscription
func (vk *VK) DonutGetSubscription(params Params) (response object.DonutDonatorSubscriptionInfo, err error) {
err = vk.RequestUnmarshal("donut.getSubscription", &response, params)
return
@ -34,7 +34,7 @@ type DonutGetSubscriptionsResponse struct {
// DonutGetSubscriptions method.
//
// https://vk.com/dev/donut.getSubscriptions
// https://dev.vk.com/method/donut.getSubscriptions
func (vk *VK) DonutGetSubscriptions(params Params) (response DonutGetSubscriptionsResponse, err error) {
err = vk.RequestUnmarshal("donut.getSubscriptions", &response, params)
return
@ -42,7 +42,7 @@ func (vk *VK) DonutGetSubscriptions(params Params) (response DonutGetSubscriptio
// DonutIsDon method.
//
// https://vk.com/dev/donut.isDon
// https://dev.vk.com/method/donut.isDon
func (vk *VK) DonutIsDon(params Params) (response int, err error) {
err = vk.RequestUnmarshal("donut.isDon", &response, params)
return

View File

@ -11,7 +11,7 @@ type DownloadedGamesGetPaidStatusResponse struct {
// DownloadedGamesGetPaidStatus method.
//
// https://vk.com/dev/downloadedGames.getPaidStatus
// https://dev.vk.com/method/downloadedGames.getPaidStatus
func (vk *VK) DownloadedGamesGetPaidStatus(params Params) (response DownloadedGamesGetPaidStatusResponse, err error) {
err = vk.RequestUnmarshal("downloadedGames.getPaidStatus", &response, params, Params{"extended": false})

View File

@ -7,15 +7,17 @@ import (
"github.com/SevereCloud/vksdk/v2/object"
)
const errorMessagePrefix = "api: "
// ErrorType is the type of an error.
type ErrorType int
// Error returns the message of a ErrorType.
func (e ErrorType) Error() string {
return fmt.Sprintf("api: error with code %d", e)
return fmt.Sprintf(errorMessagePrefix+"error with code %d", e)
}
// Error codes. See https://vk.com/dev/errors
// Error codes. See https://dev.vk.com/ru/reference/errors
const (
ErrNoType ErrorType = 0 // NoType error
@ -46,7 +48,7 @@ const (
//
// Decrease the request frequency or use the execute method.
// More details on frequency limits here:
// https://vk.com/dev/api_requests
// https://dev.vk.com/ru/api/api-requests
ErrTooMany ErrorType = 6
// Permission to perform this action is denied
@ -54,7 +56,7 @@ const (
// Make sure that your have received required permissions during the
// authorization.
// You can do it with the account.getAppPermissions method.
// https://vk.com/dev/permissions
// https://dev.vk.com/ru/reference/access-rights
ErrPermission ErrorType = 7
// Invalid request
@ -90,7 +92,7 @@ const (
// Captcha needed.
//
// See https://vk.com/dev/captcha_error
// See https://dev.vk.com/ru/api/captcha-error
ErrCaptcha ErrorType = 14
// Access denied
@ -111,7 +113,7 @@ const (
// http://vk.com/dev/auth_mobile for a request from the server.
// It's restricted.
//
// https://vk.com/dev/need_validation
// https://dev.vk.com/ru/api/validation-required-error
ErrAuthValidation ErrorType = 17
ErrUserDeleted ErrorType = 18 // User was deleted or banned
ErrBlocked ErrorType = 19 // Content blocked
@ -147,7 +149,7 @@ const (
//
// confirm = 1.
//
// https://vk.com/dev/need_confirmation
// https://dev.vk.com/ru/api/confirmation-required-error
ErrNeedConfirmation ErrorType = 24
ErrNeedTokenConfirmation ErrorType = 25 // Token confirmation required
ErrGroupAuth ErrorType = 27 // Group authorization failed
@ -155,7 +157,7 @@ const (
// Rate limit reached.
//
// More details on rate limits here: https://vk.com/dev/data_limits
// More details on rate limits here: https://dev.vk.com/ru/reference/roadmap
ErrRateLimit ErrorType = 29
ErrPrivateProfile ErrorType = 30 // This profile is private
@ -622,6 +624,12 @@ const (
// Anonymous token is invalid.
ErrAnonymousTokenInvalid ErrorType = 1116
// Access token has expired.
ErrAuthAccessTokenHasExpired ErrorType = 1117
// Anonymous token ip mismatch.
ErrAuthAnonymousTokenIPMismatch ErrorType = 1118
// Invalid document id.
ErrParamDocID ErrorType = 1150
@ -844,7 +852,7 @@ type ErrorSubtype int
// Error returns the message of a ErrorSubtype.
func (e ErrorSubtype) Error() string {
return fmt.Sprintf("api: error with subcode %d", e)
return fmt.Sprintf(errorMessagePrefix+"error with subcode %d", e)
}
// Error struct VK.
@ -872,7 +880,7 @@ type Error struct {
// confirms the action repeat the request with an extra parameter:
// confirm = 1.
//
// See https://vk.com/dev/need_confirmation
// See https://dev.vk.com/ru/api/confirmation-required-error
ConfirmationText string `json:"confirmation_text"`
// In some cases VK requires a user validation procedure. . As a result
@ -904,14 +912,14 @@ type Error struct {
//
// https://oauth.vk.com/blank.html#fail=1
//
// See https://vk.com/dev/need_validation
// See https://dev.vk.com/ru/api/validation-required-error
RedirectURI string `json:"redirect_uri"`
RequestParams []object.BaseRequestParam `json:"request_params"`
}
// Error returns the message of a Error.
func (e Error) Error() string {
return "api: " + e.Message
return errorMessagePrefix + e.Message
}
// Is unwraps its first argument sequentially looking for an error that matches
@ -968,7 +976,7 @@ type UploadError struct {
// Error returns the message of a UploadError.
func (e UploadError) Error() string {
if e.Err != "" {
return "api: " + e.Err
return errorMessagePrefix + e.Err
}
return fmt.Sprintf("api: upload code %d", e.Code)
@ -982,7 +990,7 @@ type AdsError struct {
// Error returns the message of a AdsError.
func (e AdsError) Error() string {
return "api: " + e.Desc
return errorMessagePrefix + e.Desc
}
// Is unwraps its first argument sequentially looking for an error that matches
@ -1010,7 +1018,7 @@ type AuthSilentTokenError struct {
// Error returns the description of a AuthSilentTokenError.
func (e AuthSilentTokenError) Error() string {
return "api: " + e.Description
return errorMessagePrefix + e.Description
}
// Is unwraps its first argument sequentially looking for an error that matches

View File

@ -13,10 +13,10 @@ import (
// The Args map variable allows you to retrieve the parameters passed during
// the request and avoids code formatting.
//
// return Args.code; // return parameter "code"
// return Args.v; // return parameter "v"
// return Args.code; // return parameter "code"
// return Args.v; // return parameter "v"
//
// https://vk.com/dev/execute
// https://dev.vk.com/ru/method/execute
func (vk *VK) ExecuteWithArgs(code string, params Params, obj interface{}) error {
token := vk.getToken()
@ -56,7 +56,7 @@ func (vk *VK) ExecuteWithArgs(code string, params Params, obj interface{}) error
// Execute a universal method for calling a sequence of other methods while
// saving and filtering interim results.
//
// https://vk.com/dev/execute
// https://dev.vk.com/ru/method/execute
func (vk *VK) Execute(code string, obj interface{}) error {
return vk.ExecuteWithArgs(code, Params{}, obj)
}

View File

@ -6,7 +6,7 @@ import (
// FaveAddArticle adds a link to user faves.
//
// https://vk.com/dev/fave.addArticle
// https://dev.vk.com/method/fave.addArticle
func (vk *VK) FaveAddArticle(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addArticle", &response, params)
return
@ -14,7 +14,7 @@ func (vk *VK) FaveAddArticle(params Params) (response int, err error) {
// FaveAddLink adds a link to user faves.
//
// https://vk.com/dev/fave.addLink
// https://dev.vk.com/method/fave.addLink
func (vk *VK) FaveAddLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addLink", &response, params)
return
@ -22,7 +22,7 @@ func (vk *VK) FaveAddLink(params Params) (response int, err error) {
// FaveAddPage method.
//
// https://vk.com/dev/fave.addPage
// https://dev.vk.com/method/fave.addPage
func (vk *VK) FaveAddPage(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addPage", &response, params)
return
@ -30,7 +30,7 @@ func (vk *VK) FaveAddPage(params Params) (response int, err error) {
// FaveAddPost method.
//
// https://vk.com/dev/fave.addPost
// https://dev.vk.com/method/fave.addPost
func (vk *VK) FaveAddPost(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addPost", &response, params)
return
@ -38,7 +38,7 @@ func (vk *VK) FaveAddPost(params Params) (response int, err error) {
// FaveAddProduct method.
//
// https://vk.com/dev/fave.addProduct
// https://dev.vk.com/method/fave.addProduct
func (vk *VK) FaveAddProduct(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addProduct", &response, params)
return
@ -49,7 +49,7 @@ type FaveAddTagResponse object.FaveTag
// FaveAddTag method.
//
// https://vk.com/dev/fave.addTag
// https://dev.vk.com/method/fave.addTag
func (vk *VK) FaveAddTag(params Params) (response FaveAddTagResponse, err error) {
err = vk.RequestUnmarshal("fave.addTag", &response, params)
return
@ -57,7 +57,7 @@ func (vk *VK) FaveAddTag(params Params) (response FaveAddTagResponse, err error)
// FaveAddVideo method.
//
// https://vk.com/dev/fave.addVideo
// https://dev.vk.com/method/fave.addVideo
func (vk *VK) FaveAddVideo(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addVideo", &response, params)
return
@ -65,7 +65,7 @@ func (vk *VK) FaveAddVideo(params Params) (response int, err error) {
// FaveEditTag method.
//
// https://vk.com/dev/fave.editTag
// https://dev.vk.com/method/fave.editTag
func (vk *VK) FaveEditTag(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.editTag", &response, params)
return
@ -79,9 +79,9 @@ type FaveGetResponse struct {
// FaveGet method.
//
// extended=0
// extended=0
//
// https://vk.com/dev/fave.get
// https://dev.vk.com/method/fave.get
func (vk *VK) FaveGet(params Params) (response FaveGetResponse, err error) {
err = vk.RequestUnmarshal("fave.get", &response, params, Params{"extended": false})
@ -97,9 +97,9 @@ type FaveGetExtendedResponse struct {
// FaveGetExtended method.
//
// extended=1
// extended=1
//
// https://vk.com/dev/fave.get
// https://dev.vk.com/method/fave.get
func (vk *VK) FaveGetExtended(params Params) (response FaveGetExtendedResponse, err error) {
err = vk.RequestUnmarshal("fave.get", &response, params, Params{"extended": true})
@ -114,7 +114,7 @@ type FaveGetPagesResponse struct {
// FaveGetPages method.
//
// https://vk.com/dev/fave.getPages
// https://dev.vk.com/method/fave.getPages
func (vk *VK) FaveGetPages(params Params) (response FaveGetPagesResponse, err error) {
err = vk.RequestUnmarshal("fave.getPages", &response, params)
return
@ -128,7 +128,7 @@ type FaveGetTagsResponse struct {
// FaveGetTags method.
//
// https://vk.com/dev/fave.getTags
// https://dev.vk.com/method/fave.getTags
func (vk *VK) FaveGetTags(params Params) (response FaveGetTagsResponse, err error) {
err = vk.RequestUnmarshal("fave.getTags", &response, params)
return
@ -136,7 +136,7 @@ func (vk *VK) FaveGetTags(params Params) (response FaveGetTagsResponse, err erro
// FaveMarkSeen method.
//
// https://vk.com/dev/fave.markSeen
// https://dev.vk.com/method/fave.markSeen
func (vk *VK) FaveMarkSeen(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.markSeen", &response, params)
return
@ -144,7 +144,7 @@ func (vk *VK) FaveMarkSeen(params Params) (response int, err error) {
// FaveRemoveArticle method.
//
// https://vk.com/dev/fave.removeArticle
// https://dev.vk.com/method/fave.removeArticle
func (vk *VK) FaveRemoveArticle(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeArticle", &response, params)
return
@ -152,7 +152,7 @@ func (vk *VK) FaveRemoveArticle(params Params) (response int, err error) {
// FaveRemoveLink removes link from the user's faves.
//
// https://vk.com/dev/fave.removeLink
// https://dev.vk.com/method/fave.removeLink
func (vk *VK) FaveRemoveLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeLink", &response, params)
return
@ -160,7 +160,7 @@ func (vk *VK) FaveRemoveLink(params Params) (response int, err error) {
// FaveRemovePage method.
//
// https://vk.com/dev/fave.removePage
// https://dev.vk.com/method/fave.removePage
func (vk *VK) FaveRemovePage(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removePage", &response, params)
return
@ -168,7 +168,7 @@ func (vk *VK) FaveRemovePage(params Params) (response int, err error) {
// FaveRemovePost method.
//
// https://vk.com/dev/fave.removePost
// https://dev.vk.com/method/fave.removePost
func (vk *VK) FaveRemovePost(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removePost", &response, params)
return
@ -176,7 +176,7 @@ func (vk *VK) FaveRemovePost(params Params) (response int, err error) {
// FaveRemoveProduct method.
//
// https://vk.com/dev/fave.removeProduct
// https://dev.vk.com/method/fave.removeProduct
func (vk *VK) FaveRemoveProduct(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeProduct", &response, params)
return
@ -184,7 +184,7 @@ func (vk *VK) FaveRemoveProduct(params Params) (response int, err error) {
// FaveRemoveTag method.
//
// https://vk.com/dev/fave.removeTag
// https://dev.vk.com/method/fave.removeTag
func (vk *VK) FaveRemoveTag(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeTag", &response, params)
return
@ -192,7 +192,7 @@ func (vk *VK) FaveRemoveTag(params Params) (response int, err error) {
// FaveRemoveVideo method.
//
// https://vk.com/dev/fave.removeVideo
// https://dev.vk.com/method/fave.removeVideo
func (vk *VK) FaveRemoveVideo(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeVideo", &response, params)
return
@ -200,7 +200,7 @@ func (vk *VK) FaveRemoveVideo(params Params) (response int, err error) {
// FaveReorderTags method.
//
// https://vk.com/dev/fave.reorderTags
// https://dev.vk.com/method/fave.reorderTags
func (vk *VK) FaveReorderTags(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.reorderTags", &response, params)
return
@ -208,7 +208,7 @@ func (vk *VK) FaveReorderTags(params Params) (response int, err error) {
// FaveSetPageTags method.
//
// https://vk.com/dev/fave.setPageTags
// https://dev.vk.com/method/fave.setPageTags
func (vk *VK) FaveSetPageTags(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.setPageTags", &response, params)
return
@ -216,7 +216,7 @@ func (vk *VK) FaveSetPageTags(params Params) (response int, err error) {
// FaveSetTags method.
//
// https://vk.com/dev/fave.setTags
// https://dev.vk.com/method/fave.setTags
func (vk *VK) FaveSetTags(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.setTags", &response, params)
return
@ -224,7 +224,7 @@ func (vk *VK) FaveSetTags(params Params) (response int, err error) {
// FaveTrackPageInteraction method.
//
// https://vk.com/dev/fave.trackPageInteraction
// https://dev.vk.com/method/fave.trackPageInteraction
func (vk *VK) FaveTrackPageInteraction(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.trackPageInteraction", &response, params)
return

View File

@ -6,7 +6,7 @@ import (
// FriendsAdd approves or creates a friend request.
//
// https://vk.com/dev/friends.add
// https://dev.vk.com/method/friends.add
func (vk *VK) FriendsAdd(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.add", &response, params)
return
@ -19,7 +19,7 @@ type FriendsAddListResponse struct {
// FriendsAddList creates a new friend list for the current user.
//
// https://vk.com/dev/friends.addList
// https://dev.vk.com/method/friends.addList
func (vk *VK) FriendsAddList(params Params) (response FriendsAddListResponse, err error) {
err = vk.RequestUnmarshal("friends.addList", &response, params)
return
@ -30,7 +30,7 @@ type FriendsAreFriendsResponse []object.FriendsFriendStatus
// FriendsAreFriends checks the current user's friendship status with other specified users.
//
// https://vk.com/dev/friends.areFriends
// https://dev.vk.com/method/friends.areFriends
func (vk *VK) FriendsAreFriends(params Params) (response FriendsAreFriendsResponse, err error) {
err = vk.RequestUnmarshal("friends.areFriends", &response, params)
return
@ -47,7 +47,7 @@ type FriendsDeleteResponse struct {
// FriendsDelete declines a friend request or deletes a user from the current user's friend list.
//
// https://vk.com/dev/friends.delete
// https://dev.vk.com/method/friends.delete
func (vk *VK) FriendsDelete(params Params) (response FriendsDeleteResponse, err error) {
err = vk.RequestUnmarshal("friends.delete", &response, params)
return
@ -55,7 +55,7 @@ func (vk *VK) FriendsDelete(params Params) (response FriendsDeleteResponse, err
// FriendsDeleteAllRequests marks all incoming friend requests as viewed.
//
// https://vk.com/dev/friends.deleteAllRequests
// https://dev.vk.com/method/friends.deleteAllRequests
func (vk *VK) FriendsDeleteAllRequests(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.deleteAllRequests", &response, params)
return
@ -63,7 +63,7 @@ func (vk *VK) FriendsDeleteAllRequests(params Params) (response int, err error)
// FriendsDeleteList deletes a friend list of the current user.
//
// https://vk.com/dev/friends.deleteList
// https://dev.vk.com/method/friends.deleteList
func (vk *VK) FriendsDeleteList(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.deleteList", &response, params)
return
@ -71,7 +71,7 @@ func (vk *VK) FriendsDeleteList(params Params) (response int, err error) {
// FriendsEdit edits the friend lists of the selected user.
//
// https://vk.com/dev/friends.edit
// https://dev.vk.com/method/friends.edit
func (vk *VK) FriendsEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.edit", &response, params)
return
@ -79,7 +79,7 @@ func (vk *VK) FriendsEdit(params Params) (response int, err error) {
// FriendsEditList edits a friend list of the current user.
//
// https://vk.com/dev/friends.editList
// https://dev.vk.com/method/friends.editList
func (vk *VK) FriendsEditList(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.editList", &response, params)
return
@ -93,7 +93,7 @@ type FriendsGetResponse struct {
// FriendsGet returns a list of user IDs or detailed information about a user's friends.
//
// https://vk.com/dev/friends.get
// https://dev.vk.com/method/friends.get
func (vk *VK) FriendsGet(params Params) (response FriendsGetResponse, err error) {
err = vk.RequestUnmarshal("friends.get", &response, params)
return
@ -107,7 +107,7 @@ type FriendsGetFieldsResponse struct {
// FriendsGetFields returns a list of user IDs or detailed information about a user's friends.
//
// https://vk.com/dev/friends.get
// https://dev.vk.com/method/friends.get
func (vk *VK) FriendsGetFields(params Params) (response FriendsGetFieldsResponse, err error) {
reqParams := make(Params)
if v, prs := params["fields"]; v == "" || !prs {
@ -124,7 +124,7 @@ type FriendsGetAppUsersResponse []int
// FriendsGetAppUsers returns a list of IDs of the current user's friends who installed the application.
//
// https://vk.com/dev/friends.getAppUsers
// https://dev.vk.com/method/friends.getAppUsers
func (vk *VK) FriendsGetAppUsers(params Params) (response FriendsGetAppUsersResponse, err error) {
err = vk.RequestUnmarshal("friends.getAppUsers", &response, params)
return
@ -136,7 +136,7 @@ type FriendsGetByPhonesResponse []object.FriendsUserXtrPhone
// FriendsGetByPhones returns a list of the current user's friends
// whose phone numbers, validated or specified in a profile, are in a given list.
//
// https://vk.com/dev/friends.getByPhones
// https://dev.vk.com/method/friends.getByPhones
func (vk *VK) FriendsGetByPhones(params Params) (response FriendsGetByPhonesResponse, err error) {
err = vk.RequestUnmarshal("friends.getByPhones", &response, params)
return
@ -150,7 +150,7 @@ type FriendsGetListsResponse struct {
// FriendsGetLists returns a list of the user's friend lists.
//
// https://vk.com/dev/friends.getLists
// https://dev.vk.com/method/friends.getLists
func (vk *VK) FriendsGetLists(params Params) (response FriendsGetListsResponse, err error) {
err = vk.RequestUnmarshal("friends.getLists", &response, params)
return
@ -161,7 +161,7 @@ type FriendsGetMutualResponse []int
// FriendsGetMutual returns a list of user IDs of the mutual friends of two users.
//
// https://vk.com/dev/friends.getMutual
// https://dev.vk.com/method/friends.getMutual
func (vk *VK) FriendsGetMutual(params Params) (response FriendsGetMutualResponse, err error) {
err = vk.RequestUnmarshal("friends.getMutual", &response, params)
return
@ -169,9 +169,9 @@ func (vk *VK) FriendsGetMutual(params Params) (response FriendsGetMutualResponse
// FriendsGetOnline returns a list of user IDs of a user's friends who are online.
//
// online_mobile=0
// online_mobile=0
//
// https://vk.com/dev/friends.getOnline
// https://dev.vk.com/method/friends.getOnline
func (vk *VK) FriendsGetOnline(params Params) (response []int, err error) {
err = vk.RequestUnmarshal("friends.getOnline", &response, params, Params{"online_mobile": false})
@ -186,9 +186,9 @@ type FriendsGetOnlineOnlineMobileResponse struct {
// FriendsGetOnlineOnlineMobile returns a list of user IDs of a user's friends who are online.
//
// online_mobile=1
// online_mobile=1
//
// https://vk.com/dev/friends.getOnline
// https://dev.vk.com/method/friends.getOnline
func (vk *VK) FriendsGetOnlineOnlineMobile(params Params) (response FriendsGetOnlineOnlineMobileResponse, err error) {
err = vk.RequestUnmarshal("friends.getOnline", &response, params, Params{"online_mobile": true})
@ -200,7 +200,7 @@ type FriendsGetRecentResponse []int
// FriendsGetRecent returns a list of user IDs of the current user's recently added friends.
//
// https://vk.com/dev/friends.getRecent
// https://dev.vk.com/method/friends.getRecent
func (vk *VK) FriendsGetRecent(params Params) (response FriendsGetRecentResponse, err error) {
err = vk.RequestUnmarshal("friends.getRecent", &response, params)
return
@ -214,7 +214,7 @@ type FriendsGetRequestsResponse struct {
// FriendsGetRequests returns information about the current user's incoming and outgoing friend requests.
//
// https://vk.com/dev/friends.getRequests
// https://dev.vk.com/method/friends.getRequests
func (vk *VK) FriendsGetRequests(params Params) (response FriendsGetRequestsResponse, err error) {
reqParams := Params{
"need_mutual": false,
@ -234,7 +234,7 @@ type FriendsGetRequestsNeedMutualResponse struct {
// FriendsGetRequestsNeedMutual returns information about the current user's incoming and outgoing friend requests.
//
// https://vk.com/dev/friends.getRequests
// https://dev.vk.com/method/friends.getRequests
func (vk *VK) FriendsGetRequestsNeedMutual(params Params) (response FriendsGetRequestsNeedMutualResponse, err error) {
reqParams := Params{
"extended": false,
@ -254,7 +254,7 @@ type FriendsGetRequestsExtendedResponse struct {
// FriendsGetRequestsExtended returns information about the current user's incoming and outgoing friend requests.
//
// https://vk.com/dev/friends.getRequests
// https://dev.vk.com/method/friends.getRequests
func (vk *VK) FriendsGetRequestsExtended(params Params) (response FriendsGetRequestsExtendedResponse, err error) {
reqParams := Params{
"need_mutual": false,
@ -274,7 +274,7 @@ type FriendsGetSuggestionsResponse struct {
// FriendsGetSuggestions returns a list of profiles of users whom the current user may know.
//
// https://vk.com/dev/friends.getSuggestions
// https://dev.vk.com/method/friends.getSuggestions
func (vk *VK) FriendsGetSuggestions(params Params) (response FriendsGetSuggestionsResponse, err error) {
err = vk.RequestUnmarshal("friends.getSuggestions", &response, params)
return
@ -288,7 +288,7 @@ type FriendsSearchResponse struct {
// FriendsSearch returns a list of friends matching the search criteria.
//
// https://vk.com/dev/friends.search
// https://dev.vk.com/method/friends.search
func (vk *VK) FriendsSearch(params Params) (response FriendsSearchResponse, err error) {
err = vk.RequestUnmarshal("friends.search", &response, params)
return

View File

@ -10,7 +10,7 @@ type GiftsGetResponse struct {
// GiftsGet returns a list of user gifts.
//
// https://vk.com/dev/gifts.get
// https://dev.vk.com/method/gifts.get
func (vk *VK) GiftsGet(params Params) (response GiftsGetResponse, err error) {
err = vk.RequestUnmarshal("gifts.get", &response, params)
return
@ -25,7 +25,7 @@ type GiftsGetCatalogResponse []struct {
// GiftsGetCatalog returns catalog.
//
// https://vk.com/dev/gifts.get
// https://dev.vk.com/method/gifts.get
func (vk *VK) GiftsGetCatalog(params Params) (response GiftsGetCatalogResponse, err error) {
err = vk.RequestUnmarshal("gifts.getCatalog", &response, params)
return

View File

@ -9,7 +9,7 @@ type GroupsAddAddressResponse object.GroupsAddress
// GroupsAddAddress groups.addAddress.
//
// https://vk.com/dev/groups.addAddress
// https://dev.vk.com/method/groups.addAddress
func (vk *VK) GroupsAddAddress(params Params) (response GroupsAddAddressResponse, err error) {
err = vk.RequestUnmarshal("groups.addAddress", &response, params)
return
@ -22,7 +22,7 @@ type GroupsAddCallbackServerResponse struct {
// GroupsAddCallbackServer callback API server to the community.
//
// https://vk.com/dev/groups.addCallbackServer
// https://dev.vk.com/method/groups.addCallbackServer
func (vk *VK) GroupsAddCallbackServer(params Params) (response GroupsAddCallbackServerResponse, err error) {
err = vk.RequestUnmarshal("groups.addCallbackServer", &response, params)
return
@ -33,7 +33,7 @@ type GroupsAddLinkResponse object.GroupsGroupLink
// GroupsAddLink allows to add a link to the community.
//
// https://vk.com/dev/groups.addLink
// https://dev.vk.com/method/groups.addLink
func (vk *VK) GroupsAddLink(params Params) (response GroupsAddLinkResponse, err error) {
err = vk.RequestUnmarshal("groups.addLink", &response, params)
return
@ -41,7 +41,7 @@ func (vk *VK) GroupsAddLink(params Params) (response GroupsAddLinkResponse, err
// GroupsApproveRequest allows to approve join request to the community.
//
// https://vk.com/dev/groups.approveRequest
// https://dev.vk.com/method/groups.approveRequest
func (vk *VK) GroupsApproveRequest(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.approveRequest", &response, params)
return
@ -49,7 +49,7 @@ func (vk *VK) GroupsApproveRequest(params Params) (response int, err error) {
// GroupsBan adds a user or a group to the community blacklist.
//
// https://vk.com/dev/groups.ban
// https://dev.vk.com/method/groups.ban
func (vk *VK) GroupsBan(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.ban", &response, params)
return
@ -60,7 +60,7 @@ type GroupsCreateResponse object.GroupsGroup
// GroupsCreate creates a new community.
//
// https://vk.com/dev/groups.create
// https://dev.vk.com/method/groups.create
func (vk *VK) GroupsCreate(params Params) (response GroupsCreateResponse, err error) {
err = vk.RequestUnmarshal("groups.create", &response, params)
return
@ -68,7 +68,7 @@ func (vk *VK) GroupsCreate(params Params) (response GroupsCreateResponse, err er
// GroupsDeleteAddress groups.deleteAddress.
//
// https://vk.com/dev/groups.deleteAddress
// https://dev.vk.com/method/groups.deleteAddress
func (vk *VK) GroupsDeleteAddress(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.deleteAddress", &response, params)
return
@ -76,7 +76,7 @@ func (vk *VK) GroupsDeleteAddress(params Params) (response int, err error) {
// GroupsDeleteCallbackServer callback API server from the community.
//
// https://vk.com/dev/groups.deleteCallbackServer
// https://dev.vk.com/method/groups.deleteCallbackServer
func (vk *VK) GroupsDeleteCallbackServer(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.deleteCallbackServer", &response, params)
return
@ -84,7 +84,7 @@ func (vk *VK) GroupsDeleteCallbackServer(params Params) (response int, err error
// GroupsDeleteLink allows to delete a link from the community.
//
// https://vk.com/dev/groups.deleteLink
// https://dev.vk.com/method/groups.deleteLink
func (vk *VK) GroupsDeleteLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.deleteLink", &response, params)
return
@ -92,7 +92,7 @@ func (vk *VK) GroupsDeleteLink(params Params) (response int, err error) {
// GroupsDisableOnline disables "online" status in the community.
//
// https://vk.com/dev/groups.disableOnline
// https://dev.vk.com/method/groups.disableOnline
func (vk *VK) GroupsDisableOnline(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.disableOnline", &response, params)
return
@ -100,7 +100,7 @@ func (vk *VK) GroupsDisableOnline(params Params) (response int, err error) {
// GroupsEdit edits a community.
//
// https://vk.com/dev/groups.edit
// https://dev.vk.com/method/groups.edit
func (vk *VK) GroupsEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.edit", &response, params)
return
@ -111,7 +111,7 @@ type GroupsEditAddressResponse object.GroupsAddress
// GroupsEditAddress groups.editAddress.
//
// https://vk.com/dev/groups.editAddress
// https://dev.vk.com/method/groups.editAddress
func (vk *VK) GroupsEditAddress(params Params) (response GroupsEditAddressResponse, err error) {
err = vk.RequestUnmarshal("groups.editAddress", &response, params)
return
@ -119,7 +119,7 @@ func (vk *VK) GroupsEditAddress(params Params) (response GroupsEditAddressRespon
// GroupsEditCallbackServer edits Callback API server in the community.
//
// https://vk.com/dev/groups.editCallbackServer
// https://dev.vk.com/method/groups.editCallbackServer
func (vk *VK) GroupsEditCallbackServer(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.editCallbackServer", &response, params)
return
@ -127,7 +127,7 @@ func (vk *VK) GroupsEditCallbackServer(params Params) (response int, err error)
// GroupsEditLink allows to edit a link in the community.
//
// https://vk.com/dev/groups.editLink
// https://dev.vk.com/method/groups.editLink
func (vk *VK) GroupsEditLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.editLink", &response, params)
return
@ -135,7 +135,7 @@ func (vk *VK) GroupsEditLink(params Params) (response int, err error) {
// GroupsEditManager allows to add, remove or edit the community manager .
//
// https://vk.com/dev/groups.editManager
// https://dev.vk.com/method/groups.editManager
func (vk *VK) GroupsEditManager(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.editManager", &response, params)
return
@ -143,7 +143,7 @@ func (vk *VK) GroupsEditManager(params Params) (response int, err error) {
// GroupsEnableOnline enables "online" status in the community.
//
// https://vk.com/dev/groups.enableOnline
// https://dev.vk.com/method/groups.enableOnline
func (vk *VK) GroupsEnableOnline(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.enableOnline", &response, params)
return
@ -157,9 +157,9 @@ type GroupsGetResponse struct {
// GroupsGet returns a list of the communities to which a user belongs.
//
// extended=0
// extended=0
//
// https://vk.com/dev/groups.get
// https://dev.vk.com/method/groups.get
func (vk *VK) GroupsGet(params Params) (response GroupsGetResponse, err error) {
err = vk.RequestUnmarshal("groups.get", &response, params, Params{"extended": false})
@ -174,9 +174,9 @@ type GroupsGetExtendedResponse struct {
// GroupsGetExtended returns a list of the communities to which a user belongs.
//
// extended=1
// extended=1
//
// https://vk.com/dev/groups.get
// https://dev.vk.com/method/groups.get
func (vk *VK) GroupsGetExtended(params Params) (response GroupsGetExtendedResponse, err error) {
err = vk.RequestUnmarshal("groups.get", &response, params, Params{"extended": true})
@ -191,7 +191,7 @@ type GroupsGetAddressesResponse struct {
// GroupsGetAddresses groups.getAddresses.
//
// https://vk.com/dev/groups.getAddresses
// https://dev.vk.com/method/groups.getAddresses
func (vk *VK) GroupsGetAddresses(params Params) (response GroupsGetAddressesResponse, err error) {
err = vk.RequestUnmarshal("groups.getAddresses", &response, params)
return
@ -205,7 +205,7 @@ type GroupsGetBannedResponse struct {
// GroupsGetBanned returns a list of users on a community blacklist.
//
// https://vk.com/dev/groups.getBanned
// https://dev.vk.com/method/groups.getBanned
func (vk *VK) GroupsGetBanned(params Params) (response GroupsGetBannedResponse, err error) {
err = vk.RequestUnmarshal("groups.getBanned", &response, params)
return
@ -216,7 +216,7 @@ type GroupsGetByIDResponse []object.GroupsGroup
// GroupsGetByID returns information about communities by their IDs.
//
// https://vk.com/dev/groups.getById
// https://dev.vk.com/method/groups.getById
func (vk *VK) GroupsGetByID(params Params) (response GroupsGetByIDResponse, err error) {
err = vk.RequestUnmarshal("groups.getById", &response, params)
return
@ -229,7 +229,7 @@ type GroupsGetCallbackConfirmationCodeResponse struct {
// GroupsGetCallbackConfirmationCode returns Callback API confirmation code for the community.
//
// https://vk.com/dev/groups.getCallbackConfirmationCode
// https://dev.vk.com/method/groups.getCallbackConfirmationCode
func (vk *VK) GroupsGetCallbackConfirmationCode(params Params) (
response GroupsGetCallbackConfirmationCodeResponse,
err error,
@ -246,7 +246,7 @@ type GroupsGetCallbackServersResponse struct {
// GroupsGetCallbackServers receives a list of Callback API servers from the community.
//
// https://vk.com/dev/groups.getCallbackServers
// https://dev.vk.com/method/groups.getCallbackServers
func (vk *VK) GroupsGetCallbackServers(params Params) (response GroupsGetCallbackServersResponse, err error) {
err = vk.RequestUnmarshal("groups.getCallbackServers", &response, params)
return
@ -259,7 +259,7 @@ type GroupsGetCallbackSettingsResponse object.GroupsCallbackSettings
//
// BUG(VK): MessageEdit always 0 https://vk.com/bugtracker?act=show&id=86762
//
// https://vk.com/dev/groups.getCallbackSettings
// https://dev.vk.com/method/groups.getCallbackSettings
func (vk *VK) GroupsGetCallbackSettings(params Params) (response GroupsGetCallbackSettingsResponse, err error) {
err = vk.RequestUnmarshal("groups.getCallbackSettings", &response, params)
return
@ -273,7 +273,9 @@ type GroupsGetCatalogResponse struct {
// GroupsGetCatalog returns communities list for a catalog category.
//
// https://vk.com/dev/groups.getCatalog
// Deprecated: This method is deprecated and may be disabled soon, please avoid
//
// https://dev.vk.com/method/groups.getCatalog
func (vk *VK) GroupsGetCatalog(params Params) (response GroupsGetCatalogResponse, err error) {
err = vk.RequestUnmarshal("groups.getCatalog", &response, params)
return
@ -287,9 +289,9 @@ type GroupsGetCatalogInfoResponse struct {
// GroupsGetCatalogInfo returns categories list for communities catalog.
//
// extended=0
// extended=0
//
// https://vk.com/dev/groups.getCatalogInfo
// https://dev.vk.com/method/groups.getCatalogInfo
func (vk *VK) GroupsGetCatalogInfo(params Params) (response GroupsGetCatalogInfoResponse, err error) {
err = vk.RequestUnmarshal("groups.getCatalogInfo", &response, params, Params{"extended": false})
@ -304,9 +306,9 @@ type GroupsGetCatalogInfoExtendedResponse struct {
// GroupsGetCatalogInfoExtended returns categories list for communities catalog.
//
// extended=1
// extended=1
//
// https://vk.com/dev/groups.getCatalogInfo
// https://dev.vk.com/method/groups.getCatalogInfo
func (vk *VK) GroupsGetCatalogInfoExtended(params Params) (response GroupsGetCatalogInfoExtendedResponse, err error) {
err = vk.RequestUnmarshal("groups.getCatalogInfo", &response, params, Params{"extended": true})
@ -321,7 +323,7 @@ type GroupsGetInvitedUsersResponse struct {
// GroupsGetInvitedUsers returns invited users list of a community.
//
// https://vk.com/dev/groups.getInvitedUsers
// https://dev.vk.com/method/groups.getInvitedUsers
func (vk *VK) GroupsGetInvitedUsers(params Params) (response GroupsGetInvitedUsersResponse, err error) {
err = vk.RequestUnmarshal("groups.getInvitedUsers", &response, params)
return
@ -335,7 +337,7 @@ type GroupsGetInvitesResponse struct {
// GroupsGetInvites returns a list of invitations to join communities and events.
//
// https://vk.com/dev/groups.getInvites
// https://dev.vk.com/method/groups.getInvites
func (vk *VK) GroupsGetInvites(params Params) (response GroupsGetInvitesResponse, err error) {
err = vk.RequestUnmarshal("groups.getInvites", &response, params)
return
@ -350,7 +352,7 @@ type GroupsGetInvitesExtendedResponse struct {
// GroupsGetInvitesExtended returns a list of invitations to join communities and events.
//
// https://vk.com/dev/groups.getInvites
// https://dev.vk.com/method/groups.getInvites
func (vk *VK) GroupsGetInvitesExtended(params Params) (response GroupsGetInvitesExtendedResponse, err error) {
err = vk.RequestUnmarshal("groups.getInvites", &response, params)
return
@ -361,7 +363,7 @@ type GroupsGetLongPollServerResponse object.GroupsLongPollServer
// GroupsGetLongPollServer returns data for Bots Long Poll API connection.
//
// https://vk.com/dev/groups.getLongPollServer
// https://dev.vk.com/method/groups.getLongPollServer
func (vk *VK) GroupsGetLongPollServer(params Params) (response GroupsGetLongPollServerResponse, err error) {
err = vk.RequestUnmarshal("groups.getLongPollServer", &response, params)
return
@ -372,7 +374,7 @@ type GroupsGetLongPollSettingsResponse object.GroupsLongPollSettings
// GroupsGetLongPollSettings returns Bots Long Poll API settings.
//
// https://vk.com/dev/groups.getLongPollSettings
// https://dev.vk.com/method/groups.getLongPollSettings
func (vk *VK) GroupsGetLongPollSettings(params Params) (response GroupsGetLongPollSettingsResponse, err error) {
err = vk.RequestUnmarshal("groups.getLongPollSettings", &response, params)
return
@ -386,7 +388,7 @@ type GroupsGetMembersResponse struct {
// GroupsGetMembers returns a list of community members.
//
// https://vk.com/dev/groups.getMembers
// https://dev.vk.com/method/groups.getMembers
func (vk *VK) GroupsGetMembers(params Params) (response GroupsGetMembersResponse, err error) {
err = vk.RequestUnmarshal("groups.getMembers", &response, params, Params{"filter": ""})
@ -401,7 +403,7 @@ type GroupsGetMembersFieldsResponse struct {
// GroupsGetMembersFields returns a list of community members.
//
// https://vk.com/dev/groups.getMembers
// https://dev.vk.com/method/groups.getMembers
func (vk *VK) GroupsGetMembersFields(params Params) (response GroupsGetMembersFieldsResponse, err error) {
reqParams := make(Params)
if v, prs := params["fields"]; v == "" || !prs {
@ -421,9 +423,9 @@ type GroupsGetMembersFilterManagersResponse struct {
// GroupsGetMembersFilterManagers returns a list of community members.
//
// filter=managers
// filter=managers
//
// https://vk.com/dev/groups.getMembers
// https://dev.vk.com/method/groups.getMembers
func (vk *VK) GroupsGetMembersFilterManagers(params Params) (
response GroupsGetMembersFilterManagersResponse,
err error,
@ -438,7 +440,7 @@ type GroupsGetOnlineStatusResponse object.GroupsOnlineStatus
// GroupsGetOnlineStatus returns a community's online status.
//
// https://vk.com/dev/groups.getOnlineStatus
// https://dev.vk.com/method/groups.getOnlineStatus
func (vk *VK) GroupsGetOnlineStatus(params Params) (response GroupsGetOnlineStatusResponse, err error) {
err = vk.RequestUnmarshal("groups.getOnlineStatus", &response, params)
return
@ -452,7 +454,7 @@ type GroupsGetRequestsResponse struct {
// GroupsGetRequests returns a list of requests to the community.
//
// https://vk.com/dev/groups.getRequests
// https://dev.vk.com/method/groups.getRequests
func (vk *VK) GroupsGetRequests(params Params) (response GroupsGetRequestsResponse, err error) {
err = vk.RequestUnmarshal("groups.getRequests", &response, params, Params{"fields": ""})
@ -467,7 +469,7 @@ type GroupsGetRequestsFieldsResponse struct {
// GroupsGetRequestsFields returns a list of requests to the community.
//
// https://vk.com/dev/groups.getRequests
// https://dev.vk.com/method/groups.getRequests
func (vk *VK) GroupsGetRequestsFields(params Params) (response GroupsGetRequestsFieldsResponse, err error) {
reqParams := make(Params)
if v, prs := params["fields"]; v == "" || !prs {
@ -484,7 +486,7 @@ type GroupsGetSettingsResponse object.GroupsGroupSettings
// GroupsGetSettings returns community settings.
//
// https://vk.com/dev/groups.getSettings
// https://dev.vk.com/method/groups.getSettings
func (vk *VK) GroupsGetSettings(params Params) (response GroupsGetSettingsResponse, err error) {
err = vk.RequestUnmarshal("groups.getSettings", &response, params)
return
@ -495,7 +497,7 @@ type GroupsGetTagListResponse []object.GroupsTag
// GroupsGetTagList returns community tags list.
//
// https://vk.com/dev/groups.getTagList
// https://dev.vk.com/method/groups.getTagList
func (vk *VK) GroupsGetTagList(params Params) (response GroupsGetTagListResponse, err error) {
err = vk.RequestUnmarshal("groups.getTagList", &response, params)
return
@ -506,7 +508,7 @@ type GroupsGetTokenPermissionsResponse object.GroupsTokenPermissions
// GroupsGetTokenPermissions returns permissions scope for the community's access_token.
//
// https://vk.com/dev/groups.getTokenPermissions
// https://dev.vk.com/method/groups.getTokenPermissions
func (vk *VK) GroupsGetTokenPermissions(params Params) (response GroupsGetTokenPermissionsResponse, err error) {
err = vk.RequestUnmarshal("groups.getTokenPermissions", &response, params)
return
@ -514,7 +516,7 @@ func (vk *VK) GroupsGetTokenPermissions(params Params) (response GroupsGetTokenP
// GroupsInvite allows to invite friends to the community.
//
// https://vk.com/dev/groups.invite
// https://dev.vk.com/method/groups.invite
func (vk *VK) GroupsInvite(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.invite", &response, params)
return
@ -522,9 +524,9 @@ func (vk *VK) GroupsInvite(params Params) (response int, err error) {
// GroupsIsMember returns information specifying whether a user is a member of a community.
//
// extended=0
// extended=0
//
// https://vk.com/dev/groups.isMember
// https://dev.vk.com/method/groups.isMember
func (vk *VK) GroupsIsMember(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.isMember", &response, params, Params{"extended": false})
@ -542,9 +544,9 @@ type GroupsIsMemberExtendedResponse struct {
// GroupsIsMemberExtended returns information specifying whether a user is a member of a community.
//
// extended=1
// extended=1
//
// https://vk.com/dev/groups.isMember
// https://dev.vk.com/method/groups.isMember
func (vk *VK) GroupsIsMemberExtended(params Params) (response GroupsIsMemberExtendedResponse, err error) {
err = vk.RequestUnmarshal("groups.isMember", &response, params, Params{"extended": true})
@ -556,10 +558,10 @@ type GroupsIsMemberUserIDsExtendedResponse []object.GroupsMemberStatusFull
// GroupsIsMemberUserIDsExtended returns information specifying whether a user is a member of a community.
//
// extended=1
// need user_ids
// extended=1
// need user_ids
//
// https://vk.com/dev/groups.isMember
// https://dev.vk.com/method/groups.isMember
func (vk *VK) GroupsIsMemberUserIDsExtended(params Params) (response GroupsIsMemberUserIDsExtendedResponse, err error) {
err = vk.RequestUnmarshal("groups.isMember", &response, params, Params{"extended": true})
@ -571,10 +573,10 @@ type GroupsIsMemberUserIDsResponse []object.GroupsMemberStatus
// GroupsIsMemberUserIDs returns information specifying whether a user is a member of a community.
//
// extended=0
// need user_ids
// extended=0
// need user_ids
//
// https://vk.com/dev/groups.isMember
// https://dev.vk.com/method/groups.isMember
func (vk *VK) GroupsIsMemberUserIDs(params Params) (response GroupsIsMemberUserIDsResponse, err error) {
err = vk.RequestUnmarshal("groups.isMember", &response, params, Params{"extended": false})
@ -583,7 +585,7 @@ func (vk *VK) GroupsIsMemberUserIDs(params Params) (response GroupsIsMemberUserI
// GroupsJoin with this method you can join the group or public page, and also confirm your participation in an event.
//
// https://vk.com/dev/groups.join
// https://dev.vk.com/method/groups.join
func (vk *VK) GroupsJoin(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.join", &response, params)
return
@ -591,7 +593,7 @@ func (vk *VK) GroupsJoin(params Params) (response int, err error) {
// GroupsLeave with this method you can leave a group, public page, or event.
//
// https://vk.com/dev/groups.leave
// https://dev.vk.com/method/groups.leave
func (vk *VK) GroupsLeave(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.leave", &response, params)
return
@ -599,7 +601,7 @@ func (vk *VK) GroupsLeave(params Params) (response int, err error) {
// GroupsRemoveUser removes a user from the community.
//
// https://vk.com/dev/groups.removeUser
// https://dev.vk.com/method/groups.removeUser
func (vk *VK) GroupsRemoveUser(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.removeUser", &response, params)
return
@ -607,7 +609,7 @@ func (vk *VK) GroupsRemoveUser(params Params) (response int, err error) {
// GroupsReorderLink allows to reorder links in the community.
//
// https://vk.com/dev/groups.reorderLink
// https://dev.vk.com/method/groups.reorderLink
func (vk *VK) GroupsReorderLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.reorderLink", &response, params)
return
@ -621,7 +623,7 @@ type GroupsSearchResponse struct {
// GroupsSearch returns a list of communities matching the search criteria.
//
// https://vk.com/dev/groups.search
// https://dev.vk.com/method/groups.search
func (vk *VK) GroupsSearch(params Params) (response GroupsSearchResponse, err error) {
err = vk.RequestUnmarshal("groups.search", &response, params)
return
@ -629,7 +631,7 @@ func (vk *VK) GroupsSearch(params Params) (response GroupsSearchResponse, err er
// GroupsSetCallbackSettings allow to set notifications settings for Callback API.
//
// https://vk.com/dev/groups.setCallbackSettings
// https://dev.vk.com/method/groups.setCallbackSettings
func (vk *VK) GroupsSetCallbackSettings(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.setCallbackSettings", &response, params)
return
@ -637,7 +639,7 @@ func (vk *VK) GroupsSetCallbackSettings(params Params) (response int, err error)
// GroupsSetLongPollSettings allows to set Bots Long Poll API settings in the community.
//
// https://vk.com/dev/groups.setLongPollSettings
// https://dev.vk.com/method/groups.setLongPollSettings
func (vk *VK) GroupsSetLongPollSettings(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.setLongPollSettings", &response, params)
return
@ -645,7 +647,7 @@ func (vk *VK) GroupsSetLongPollSettings(params Params) (response int, err error)
// GroupsSetSettings sets community settings.
//
// https://vk.com/dev/groups.setSettings
// https://dev.vk.com/method/groups.setSettings
func (vk *VK) GroupsSetSettings(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.setSettings", &response, params)
return
@ -654,7 +656,7 @@ func (vk *VK) GroupsSetSettings(params Params) (response int, err error) {
// GroupsSetUserNote allows to create or edit a note about a user as part
// of the user's correspondence with the community.
//
// https://vk.com/dev/groups.setUserNote
// https://dev.vk.com/method/groups.setUserNote
func (vk *VK) GroupsSetUserNote(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.setUserNote", &response, params)
return
@ -662,7 +664,7 @@ func (vk *VK) GroupsSetUserNote(params Params) (response int, err error) {
// GroupsTagAdd allows to add a new tag to the community.
//
// https://vk.com/dev/groups.tagAdd
// https://dev.vk.com/method/groups.tagAdd
func (vk *VK) GroupsTagAdd(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.tagAdd", &response, params)
return
@ -670,7 +672,7 @@ func (vk *VK) GroupsTagAdd(params Params) (response int, err error) {
// GroupsTagBind allows to "bind" and "unbind" community tags to conversations.
//
// https://vk.com/dev/groups.tagBind
// https://dev.vk.com/method/groups.tagBind
func (vk *VK) GroupsTagBind(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.tagBind", &response, params)
return
@ -681,7 +683,7 @@ func (vk *VK) GroupsTagBind(params Params) (response int, err error) {
// The remote tag will be automatically "unbind" from all conversations to
// which it was "bind" earlier.
//
// https://vk.com/dev/groups.tagDelete
// https://dev.vk.com/method/groups.tagDelete
func (vk *VK) GroupsTagDelete(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.tagDelete", &response, params)
return
@ -689,7 +691,7 @@ func (vk *VK) GroupsTagDelete(params Params) (response int, err error) {
// GroupsTagUpdate allows to change an existing tag.
//
// https://vk.com/dev/groups.tagUpdate
// https://dev.vk.com/method/groups.tagUpdate
func (vk *VK) GroupsTagUpdate(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.tagUpdate", &response, params)
return
@ -697,7 +699,7 @@ func (vk *VK) GroupsTagUpdate(params Params) (response int, err error) {
// GroupsToggleMarket method.
//
// https://vk.com/dev/groups.toggleMarket
// https://dev.vk.com/method/groups.toggleMarket
func (vk *VK) GroupsToggleMarket(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.toggleMarket", &response, params)
return
@ -705,7 +707,7 @@ func (vk *VK) GroupsToggleMarket(params Params) (response int, err error) {
// GroupsUnban groups.unban.
//
// https://vk.com/dev/groups.unban
// https://dev.vk.com/method/groups.unban
func (vk *VK) GroupsUnban(params Params) (response int, err error) {
err = vk.RequestUnmarshal("groups.unban", &response, params)
return

View File

@ -12,7 +12,7 @@ type LeadFormsCreateResponse struct {
// LeadFormsCreate leadForms.create.
//
// https://vk.com/dev/leadForms.create
// https://dev.vk.com/method/leadForms.create
func (vk *VK) LeadFormsCreate(params Params) (response LeadFormsCreateResponse, err error) {
err = vk.RequestUnmarshal("leadForms.create", &response, params)
return
@ -25,7 +25,7 @@ type LeadFormsDeleteResponse struct {
// LeadFormsDelete leadForms.delete.
//
// https://vk.com/dev/leadForms.delete
// https://dev.vk.com/method/leadForms.delete
func (vk *VK) LeadFormsDelete(params Params) (response LeadFormsDeleteResponse, err error) {
err = vk.RequestUnmarshal("leadForms.delete", &response, params)
return
@ -36,7 +36,7 @@ type LeadFormsGetResponse object.LeadFormsForm
// LeadFormsGet leadForms.get.
//
// https://vk.com/dev/leadForms.get
// https://dev.vk.com/method/leadForms.get
func (vk *VK) LeadFormsGet(params Params) (response LeadFormsGetResponse, err error) {
err = vk.RequestUnmarshal("leadForms.get", &response, params)
return
@ -49,7 +49,7 @@ type LeadFormsGetLeadsResponse struct {
// LeadFormsGetLeads leadForms.getLeads.
//
// https://vk.com/dev/leadForms.getLeads
// https://dev.vk.com/method/leadForms.getLeads
func (vk *VK) LeadFormsGetLeads(params Params) (response LeadFormsGetLeadsResponse, err error) {
err = vk.RequestUnmarshal("leadForms.getLeads", &response, params)
return
@ -57,7 +57,7 @@ func (vk *VK) LeadFormsGetLeads(params Params) (response LeadFormsGetLeadsRespon
// LeadFormsGetUploadURL leadForms.getUploadURL.
//
// https://vk.com/dev/leadForms.getUploadURL
// https://dev.vk.com/method/leadForms.getUploadURL
func (vk *VK) LeadFormsGetUploadURL(params Params) (response string, err error) {
err = vk.RequestUnmarshal("leadForms.getUploadURL", &response, params)
return
@ -68,7 +68,7 @@ type LeadFormsListResponse []object.LeadFormsForm
// LeadFormsList leadForms.list.
//
// https://vk.com/dev/leadForms.list
// https://dev.vk.com/method/leadForms.list
func (vk *VK) LeadFormsList(params Params) (response LeadFormsListResponse, err error) {
err = vk.RequestUnmarshal("leadForms.list", &response, params)
return
@ -82,7 +82,7 @@ type LeadFormsUpdateResponse struct {
// LeadFormsUpdate leadForms.update.
//
// https://vk.com/dev/leadForms.update
// https://dev.vk.com/method/leadForms.update
func (vk *VK) LeadFormsUpdate(params Params) (response LeadFormsUpdateResponse, err error) {
err = vk.RequestUnmarshal("leadForms.update", &response, params)
return

View File

@ -9,7 +9,7 @@ type LeadsCheckUserResponse object.LeadsChecked
// LeadsCheckUser checks if the user can start the lead.
//
// https://vk.com/dev/leads.checkUser
// https://dev.vk.com/method/leads.checkUser
func (vk *VK) LeadsCheckUser(params Params) (response LeadsCheckUserResponse, err error) {
err = vk.RequestUnmarshal("leads.checkUser", &response, params)
return
@ -20,7 +20,7 @@ type LeadsCompleteResponse object.LeadsComplete
// LeadsComplete completes the lead started by user.
//
// https://vk.com/dev/leads.complete
// https://dev.vk.com/method/leads.complete
func (vk *VK) LeadsComplete(params Params) (response LeadsCompleteResponse, err error) {
err = vk.RequestUnmarshal("leads.complete", &response, params)
return
@ -31,7 +31,7 @@ type LeadsGetStatsResponse object.LeadsLead
// LeadsGetStats returns lead stats data.
//
// https://vk.com/dev/leads.getStats
// https://dev.vk.com/method/leads.getStats
func (vk *VK) LeadsGetStats(params Params) (response LeadsGetStatsResponse, err error) {
err = vk.RequestUnmarshal("leads.getStats", &response, params)
return
@ -42,7 +42,7 @@ type LeadsGetUsersResponse object.LeadsEntry
// LeadsGetUsers returns a list of last user actions for the offer.
//
// https://vk.com/dev/leads.getUsers
// https://dev.vk.com/method/leads.getUsers
func (vk *VK) LeadsGetUsers(params Params) (response LeadsGetUsersResponse, err error) {
err = vk.RequestUnmarshal("leads.getUsers", &response, params)
return
@ -56,7 +56,7 @@ type LeadsMetricHitResponse struct {
// LeadsMetricHit counts the metric event.
//
// https://vk.com/dev/leads.metricHit
// https://dev.vk.com/method/leads.metricHit
func (vk *VK) LeadsMetricHit(params Params) (response LeadsMetricHitResponse, err error) {
err = vk.RequestUnmarshal("leads.metricHit", &response, params)
return
@ -67,7 +67,7 @@ type LeadsStartResponse object.LeadsStart
// LeadsStart creates new session for the user passing the offer.
//
// https://vk.com/dev/leads.start
// https://dev.vk.com/method/leads.start
func (vk *VK) LeadsStart(params Params) (response LeadsStartResponse, err error) {
err = vk.RequestUnmarshal("leads.start", &response, params)
return

View File

@ -11,7 +11,7 @@ type LikesAddResponse struct {
// LikesAdd adds the specified object to the Likes list of the current user.
//
// https://vk.com/dev/likes.add
// https://dev.vk.com/method/likes.add
func (vk *VK) LikesAdd(params Params) (response LikesAddResponse, err error) {
err = vk.RequestUnmarshal("likes.add", &response, params)
return
@ -24,7 +24,7 @@ type LikesDeleteResponse struct {
// LikesDelete deletes the specified object from the Likes list of the current user.
//
// https://vk.com/dev/likes.delete
// https://dev.vk.com/method/likes.delete
func (vk *VK) LikesDelete(params Params) (response LikesDeleteResponse, err error) {
err = vk.RequestUnmarshal("likes.delete", &response, params)
return
@ -38,9 +38,9 @@ type LikesGetListResponse struct {
// LikesGetList likes.getList returns a list of IDs of users who added the specified object to their Likes list.
//
// extended=0
// extended=0
//
// https://vk.com/dev/likes.getList
// https://dev.vk.com/method/likes.getList
func (vk *VK) LikesGetList(params Params) (response LikesGetListResponse, err error) {
err = vk.RequestUnmarshal("likes.getList", &response, params, Params{"extended": false})
@ -55,9 +55,9 @@ type LikesGetListExtendedResponse struct {
// LikesGetListExtended likes.getList returns a list of IDs of users who added the specified object to their Likes list.
//
// extended=1
// extended=1
//
// https://vk.com/dev/likes.getList
// https://dev.vk.com/method/likes.getList
func (vk *VK) LikesGetListExtended(params Params) (response LikesGetListExtendedResponse, err error) {
err = vk.RequestUnmarshal("likes.getList", &response, params, Params{"extended": true})
@ -72,7 +72,7 @@ type LikesIsLikedResponse struct {
// LikesIsLiked checks for the object in the Likes list of the specified user.
//
// https://vk.com/dev/likes.isLiked
// https://dev.vk.com/method/likes.isLiked
func (vk *VK) LikesIsLiked(params Params) (response LikesIsLikedResponse, err error) {
err = vk.RequestUnmarshal("likes.isLiked", &response, params)
return

View File

@ -11,7 +11,7 @@ type MarketAddResponse struct {
// MarketAdd adds a new item to the market.
//
// https://vk.com/dev/market.add
// https://dev.vk.com/method/market.add
func (vk *VK) MarketAdd(params Params) (response MarketAddResponse, err error) {
err = vk.RequestUnmarshal("market.add", &response, params)
return
@ -25,7 +25,7 @@ type MarketAddAlbumResponse struct {
// MarketAddAlbum creates new collection of items.
//
// https://vk.com/dev/market.addAlbum
// https://dev.vk.com/method/market.addAlbum
func (vk *VK) MarketAddAlbum(params Params) (response MarketAddAlbumResponse, err error) {
err = vk.RequestUnmarshal("market.addAlbum", &response, params)
return
@ -33,7 +33,7 @@ func (vk *VK) MarketAddAlbum(params Params) (response MarketAddAlbumResponse, er
// MarketAddToAlbum adds an item to one or multiple collections.
//
// https://vk.com/dev/market.addToAlbum
// https://dev.vk.com/method/market.addToAlbum
func (vk *VK) MarketAddToAlbum(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.addToAlbum", &response, params)
return
@ -41,7 +41,7 @@ func (vk *VK) MarketAddToAlbum(params Params) (response int, err error) {
// MarketCreateComment creates a new comment for an item.
//
// https://vk.com/dev/market.createComment
// https://dev.vk.com/method/market.createComment
func (vk *VK) MarketCreateComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.createComment", &response, params)
return
@ -49,7 +49,7 @@ func (vk *VK) MarketCreateComment(params Params) (response int, err error) {
// MarketDelete deletes an item.
//
// https://vk.com/dev/market.delete
// https://dev.vk.com/method/market.delete
func (vk *VK) MarketDelete(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.delete", &response, params)
return
@ -57,7 +57,7 @@ func (vk *VK) MarketDelete(params Params) (response int, err error) {
// MarketDeleteAlbum deletes a collection of items.
//
// https://vk.com/dev/market.deleteAlbum
// https://dev.vk.com/method/market.deleteAlbum
func (vk *VK) MarketDeleteAlbum(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.deleteAlbum", &response, params)
return
@ -65,7 +65,7 @@ func (vk *VK) MarketDeleteAlbum(params Params) (response int, err error) {
// MarketDeleteComment deletes an item's comment.
//
// https://vk.com/dev/market.deleteComment
// https://dev.vk.com/method/market.deleteComment
func (vk *VK) MarketDeleteComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.deleteComment", &response, params)
return
@ -73,7 +73,7 @@ func (vk *VK) MarketDeleteComment(params Params) (response int, err error) {
// MarketEdit edits an item.
//
// https://vk.com/dev/market.edit
// https://dev.vk.com/method/market.edit
func (vk *VK) MarketEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.edit", &response, params)
return
@ -81,7 +81,7 @@ func (vk *VK) MarketEdit(params Params) (response int, err error) {
// MarketEditAlbum edits a collection of items.
//
// https://vk.com/dev/market.editAlbum
// https://dev.vk.com/method/market.editAlbum
func (vk *VK) MarketEditAlbum(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.editAlbum", &response, params)
return
@ -89,7 +89,7 @@ func (vk *VK) MarketEditAlbum(params Params) (response int, err error) {
// MarketEditComment changes item comment's text.
//
// https://vk.com/dev/market.editComment
// https://dev.vk.com/method/market.editComment
func (vk *VK) MarketEditComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.editComment", &response, params)
return
@ -97,7 +97,7 @@ func (vk *VK) MarketEditComment(params Params) (response int, err error) {
// MarketEditOrder edits an order.
//
// https://vk.com/dev/market.editOrder
// https://dev.vk.com/method/market.editOrder
func (vk *VK) MarketEditOrder(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.editOrder", &response, params)
return
@ -111,7 +111,7 @@ type MarketGetResponse struct {
// MarketGet returns items list for a community.
//
// https://vk.com/dev/market.get
// https://dev.vk.com/method/market.get
func (vk *VK) MarketGet(params Params) (response MarketGetResponse, err error) {
err = vk.RequestUnmarshal("market.get", &response, params)
return
@ -125,7 +125,7 @@ type MarketGetAlbumByIDResponse struct {
// MarketGetAlbumByID returns items album's data.
//
// https://vk.com/dev/market.getAlbumById
// https://dev.vk.com/method/market.getAlbumById
func (vk *VK) MarketGetAlbumByID(params Params) (response MarketGetAlbumByIDResponse, err error) {
err = vk.RequestUnmarshal("market.getAlbumById", &response, params)
return
@ -139,7 +139,7 @@ type MarketGetAlbumsResponse struct {
// MarketGetAlbums returns community's collections list.
//
// https://vk.com/dev/market.getAlbums
// https://dev.vk.com/method/market.getAlbums
func (vk *VK) MarketGetAlbums(params Params) (response MarketGetAlbumsResponse, err error) {
err = vk.RequestUnmarshal("market.getAlbums", &response, params)
return
@ -153,7 +153,7 @@ type MarketGetByIDResponse struct {
// MarketGetByID returns information about market items by their iDs.
//
// https://vk.com/dev/market.getById
// https://dev.vk.com/method/market.getById
func (vk *VK) MarketGetByID(params Params) (response MarketGetByIDResponse, err error) {
err = vk.RequestUnmarshal("market.getById", &response, params)
return
@ -167,7 +167,7 @@ type MarketGetCategoriesResponse struct {
// MarketGetCategories returns a list of market categories.
//
// https://vk.com/dev/market.getCategories
// https://dev.vk.com/method/market.getCategories
func (vk *VK) MarketGetCategories(params Params) (response MarketGetCategoriesResponse, err error) {
err = vk.RequestUnmarshal("market.getCategories", &response, params)
return
@ -181,9 +181,9 @@ type MarketGetCommentsResponse struct {
// MarketGetComments returns comments list for an item.
//
// extended=0
// extended=0
//
// https://vk.com/dev/market.getComments
// https://dev.vk.com/method/market.getComments
func (vk *VK) MarketGetComments(params Params) (response MarketGetCommentsResponse, err error) {
err = vk.RequestUnmarshal("market.getComments", &response, params, Params{"extended": false})
@ -199,9 +199,9 @@ type MarketGetCommentsExtendedResponse struct {
// MarketGetCommentsExtended returns comments list for an item.
//
// extended=1
// extended=1
//
// https://vk.com/dev/market.getComments
// https://dev.vk.com/method/market.getComments
func (vk *VK) MarketGetCommentsExtended(params Params) (response MarketGetCommentsExtendedResponse, err error) {
err = vk.RequestUnmarshal("market.getComments", &response, params, Params{"extended": true})
@ -216,7 +216,7 @@ type MarketGetGroupOrdersResponse struct {
// MarketGetGroupOrders returns community's orders list.
//
// https://vk.com/dev/market.getGroupOrders
// https://dev.vk.com/method/market.getGroupOrders
func (vk *VK) MarketGetGroupOrders(params Params) (response MarketGetGroupOrdersResponse, err error) {
err = vk.RequestUnmarshal("market.getGroupOrders", &response, params)
return
@ -229,7 +229,7 @@ type MarketGetOrderByIDResponse struct {
// MarketGetOrderByID returns order by id.
//
// https://vk.com/dev/market.getOrderById
// https://dev.vk.com/method/market.getOrderById
func (vk *VK) MarketGetOrderByID(params Params) (response MarketGetOrderByIDResponse, err error) {
err = vk.RequestUnmarshal("market.getOrderById", &response, params)
return
@ -243,7 +243,7 @@ type MarketGetOrderItemsResponse struct {
// MarketGetOrderItems returns items of an order.
//
// https://vk.com/dev/market.getOrderItems
// https://dev.vk.com/method/market.getOrderItems
func (vk *VK) MarketGetOrderItems(params Params) (response MarketGetOrderItemsResponse, err error) {
err = vk.RequestUnmarshal("market.getOrderItems", &response, params)
return
@ -251,7 +251,7 @@ func (vk *VK) MarketGetOrderItems(params Params) (response MarketGetOrderItemsRe
// MarketRemoveFromAlbum removes an item from one or multiple collections.
//
// https://vk.com/dev/market.removeFromAlbum
// https://dev.vk.com/method/market.removeFromAlbum
func (vk *VK) MarketRemoveFromAlbum(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.removeFromAlbum", &response, params)
return
@ -259,7 +259,7 @@ func (vk *VK) MarketRemoveFromAlbum(params Params) (response int, err error) {
// MarketReorderAlbums reorders the collections list.
//
// https://vk.com/dev/market.reorderAlbums
// https://dev.vk.com/method/market.reorderAlbums
func (vk *VK) MarketReorderAlbums(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.reorderAlbums", &response, params)
return
@ -267,7 +267,7 @@ func (vk *VK) MarketReorderAlbums(params Params) (response int, err error) {
// MarketReorderItems changes item place in a collection.
//
// https://vk.com/dev/market.reorderItems
// https://dev.vk.com/method/market.reorderItems
func (vk *VK) MarketReorderItems(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.reorderItems", &response, params)
return
@ -275,7 +275,7 @@ func (vk *VK) MarketReorderItems(params Params) (response int, err error) {
// MarketReport sends a complaint to the item.
//
// https://vk.com/dev/market.report
// https://dev.vk.com/method/market.report
func (vk *VK) MarketReport(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.report", &response, params)
return
@ -283,7 +283,7 @@ func (vk *VK) MarketReport(params Params) (response int, err error) {
// MarketReportComment sends a complaint to the item's comment.
//
// https://vk.com/dev/market.reportComment
// https://dev.vk.com/method/market.reportComment
func (vk *VK) MarketReportComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.reportComment", &response, params)
return
@ -291,7 +291,7 @@ func (vk *VK) MarketReportComment(params Params) (response int, err error) {
// MarketRestore restores recently deleted item.
//
// https://vk.com/dev/market.restore
// https://dev.vk.com/method/market.restore
func (vk *VK) MarketRestore(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.restore", &response, params)
return
@ -299,7 +299,7 @@ func (vk *VK) MarketRestore(params Params) (response int, err error) {
// MarketRestoreComment restores a recently deleted comment.
//
// https://vk.com/dev/market.restoreComment
// https://dev.vk.com/method/market.restoreComment
func (vk *VK) MarketRestoreComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("market.restoreComment", &response, params)
return
@ -314,7 +314,7 @@ type MarketSearchResponse struct {
// MarketSearch searches market items in a community's catalog.
//
// https://vk.com/dev/market.search
// https://dev.vk.com/method/market.search
func (vk *VK) MarketSearch(params Params) (response MarketSearchResponse, err error) {
err = vk.RequestUnmarshal("market.search", &response, params)
return
@ -330,7 +330,7 @@ type MarketSearchItemsResponse struct {
// MarketSearchItems method.
//
// https://vk.com/dev/market.searchItems
// https://dev.vk.com/method/market.searchItems
func (vk *VK) MarketSearchItems(params Params) (response MarketSearchItemsResponse, err error) {
err = vk.RequestUnmarshal("market.searchItems", &response, params)
return

View File

@ -11,7 +11,7 @@ type MarusiaGetPictureUploadLinkResponse struct {
// MarusiaGetPictureUploadLink method.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaGetPictureUploadLink(params Params) (response MarusiaGetPictureUploadLinkResponse, err error) {
err = vk.RequestUnmarshal("marusia.getPictureUploadLink", &response, params)
return
@ -25,7 +25,7 @@ type MarusiaSavePictureResponse struct {
// MarusiaSavePicture method.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaSavePicture(params Params) (response MarusiaSavePictureResponse, err error) {
err = vk.RequestUnmarshal("marusia.savePicture", &response, params)
return
@ -39,7 +39,7 @@ type MarusiaGetPicturesResponse struct {
// MarusiaGetPictures method.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaGetPictures(params Params) (response MarusiaGetPicturesResponse, err error) {
err = vk.RequestUnmarshal("marusia.getPictures", &response, params)
return
@ -47,7 +47,7 @@ func (vk *VK) MarusiaGetPictures(params Params) (response MarusiaGetPicturesResp
// MarusiaDeletePicture delete picture.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaDeletePicture(params Params) (response int, err error) {
err = vk.RequestUnmarshal("marusia.deletePicture", &response, params)
return
@ -60,7 +60,7 @@ type MarusiaGetAudioUploadLinkResponse struct {
// MarusiaGetAudioUploadLink method.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaGetAudioUploadLink(params Params) (response MarusiaGetAudioUploadLinkResponse, err error) {
err = vk.RequestUnmarshal("marusia.getAudioUploadLink", &response, params)
return
@ -74,7 +74,7 @@ type MarusiaCreateAudioResponse struct {
// MarusiaCreateAudio method.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaCreateAudio(params Params) (response MarusiaCreateAudioResponse, err error) {
err = vk.RequestUnmarshal("marusia.createAudio", &response, params)
return
@ -88,7 +88,7 @@ type MarusiaGetAudiosResponse struct {
// MarusiaGetAudios method.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaGetAudios(params Params) (response MarusiaGetAudiosResponse, err error) {
err = vk.RequestUnmarshal("marusia.getAudios", &response, params)
return
@ -96,7 +96,7 @@ func (vk *VK) MarusiaGetAudios(params Params) (response MarusiaGetAudiosResponse
// MarusiaDeleteAudio delete audio.
//
// https://vk.com/dev/marusia_skill_docs10
// https://dev.vk.com/ru/marusia/media-api
func (vk *VK) MarusiaDeleteAudio(params Params) (response int, err error) {
err = vk.RequestUnmarshal("marusia.deleteAudio", &response, params)
return

View File

@ -9,7 +9,7 @@ import (
// MessagesAddChatUser adds a new user to a chat.
//
// https://vk.com/dev/messages.addChatUser
// https://dev.vk.com/method/messages.addChatUser
func (vk *VK) MessagesAddChatUser(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.addChatUser", &response, params)
return
@ -17,7 +17,7 @@ func (vk *VK) MessagesAddChatUser(params Params) (response int, err error) {
// MessagesAllowMessagesFromGroup allows sending messages from community to the current user.
//
// https://vk.com/dev/messages.allowMessagesFromGroup
// https://dev.vk.com/method/messages.allowMessagesFromGroup
func (vk *VK) MessagesAllowMessagesFromGroup(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.allowMessagesFromGroup", &response, params)
return
@ -25,7 +25,7 @@ func (vk *VK) MessagesAllowMessagesFromGroup(params Params) (response int, err e
// MessagesCreateChat creates a chat with several participants.
//
// https://vk.com/dev/messages.createChat
// https://dev.vk.com/method/messages.createChat
func (vk *VK) MessagesCreateChat(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.createChat", &response, params)
return
@ -58,7 +58,7 @@ func (resp *MessagesDeleteResponse) DecodeMsgpack(dec *msgpack.Decoder) error {
// MessagesDelete deletes one or more messages.
//
// https://vk.com/dev/messages.delete
// https://dev.vk.com/method/messages.delete
func (vk *VK) MessagesDelete(params Params) (response MessagesDeleteResponse, err error) {
err = vk.RequestUnmarshal("messages.delete", &response, params)
@ -73,7 +73,7 @@ type MessagesDeleteChatPhotoResponse struct {
// MessagesDeleteChatPhoto deletes a chat's cover picture.
//
// https://vk.com/dev/messages.deleteChatPhoto
// https://dev.vk.com/method/messages.deleteChatPhoto
func (vk *VK) MessagesDeleteChatPhoto(params Params) (response MessagesDeleteChatPhotoResponse, err error) {
err = vk.RequestUnmarshal("messages.deleteChatPhoto", &response, params)
return
@ -86,7 +86,7 @@ type MessagesDeleteConversationResponse struct {
// MessagesDeleteConversation deletes private messages in a conversation.
//
// https://vk.com/dev/messages.deleteConversation
// https://dev.vk.com/method/messages.deleteConversation
func (vk *VK) MessagesDeleteConversation(params Params) (response MessagesDeleteConversationResponse, err error) {
err = vk.RequestUnmarshal("messages.deleteConversation", &response, params)
return
@ -94,7 +94,7 @@ func (vk *VK) MessagesDeleteConversation(params Params) (response MessagesDelete
// MessagesDenyMessagesFromGroup denies sending message from community to the current user.
//
// https://vk.com/dev/messages.denyMessagesFromGroup
// https://dev.vk.com/method/messages.denyMessagesFromGroup
func (vk *VK) MessagesDenyMessagesFromGroup(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.denyMessagesFromGroup", &response, params)
return
@ -102,7 +102,7 @@ func (vk *VK) MessagesDenyMessagesFromGroup(params Params) (response int, err er
// MessagesEdit edits the message.
//
// https://vk.com/dev/messages.edit
// https://dev.vk.com/method/messages.edit
func (vk *VK) MessagesEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.edit", &response, params)
return
@ -110,12 +110,22 @@ func (vk *VK) MessagesEdit(params Params) (response int, err error) {
// MessagesEditChat edits the title of a chat.
//
// https://vk.com/dev/messages.editChat
// https://dev.vk.com/method/messages.editChat
func (vk *VK) MessagesEditChat(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.editChat", &response, params)
return
}
// MessagesForceCallFinish method.
//
// Deprecated: Use CallsForceFinish
//
// https://dev.vk.com/method/messages.forceCallFinish
func (vk *VK) MessagesForceCallFinish(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.forceCallFinish", &response, params)
return
}
// MessagesGetByConversationMessageIDResponse struct.
type MessagesGetByConversationMessageIDResponse struct {
Count int `json:"count"`
@ -125,7 +135,7 @@ type MessagesGetByConversationMessageIDResponse struct {
// MessagesGetByConversationMessageID messages.getByConversationMessageId.
//
// https://vk.com/dev/messages.getByConversationMessageId
// https://dev.vk.com/method/messages.getByConversationMessageId
func (vk *VK) MessagesGetByConversationMessageID(params Params) (
response MessagesGetByConversationMessageIDResponse,
err error,
@ -142,9 +152,9 @@ type MessagesGetByIDResponse struct {
// MessagesGetByID returns messages by their IDs.
//
// extended=0
// extended=0
//
// https://vk.com/dev/messages.getById
// https://dev.vk.com/method/messages.getById
func (vk *VK) MessagesGetByID(params Params) (response MessagesGetByIDResponse, err error) {
err = vk.RequestUnmarshal("messages.getById", &response, params, Params{"extended": false})
@ -160,9 +170,9 @@ type MessagesGetByIDExtendedResponse struct {
// MessagesGetByIDExtended returns messages by their IDs.
//
// extended=1
// extended=1
//
// https://vk.com/dev/messages.getById
// https://dev.vk.com/method/messages.getById
func (vk *VK) MessagesGetByIDExtended(params Params) (response MessagesGetByIDExtendedResponse, err error) {
err = vk.RequestUnmarshal("messages.getById", &response, params, Params{"extended": true})
@ -174,7 +184,7 @@ type MessagesGetChatResponse object.MessagesChat
// MessagesGetChat returns information about a chat.
//
// https://vk.com/dev/messages.getChat
// https://dev.vk.com/method/messages.getChat
func (vk *VK) MessagesGetChat(params Params) (response MessagesGetChatResponse, err error) {
err = vk.RequestUnmarshal("messages.getChat", &response, params)
return
@ -185,7 +195,7 @@ type MessagesGetChatChatIDsResponse []object.MessagesChat
// MessagesGetChatChatIDs returns information about a chat.
//
// https://vk.com/dev/messages.getChat
// https://dev.vk.com/method/messages.getChat
func (vk *VK) MessagesGetChatChatIDs(params Params) (response MessagesGetChatChatIDsResponse, err error) {
err = vk.RequestUnmarshal("messages.getChat", &response, params)
return
@ -199,7 +209,7 @@ type MessagesGetChatPreviewResponse struct {
// MessagesGetChatPreview allows to receive chat preview by the invitation link.
//
// https://vk.com/dev/messages.getChatPreview
// https://dev.vk.com/method/messages.getChatPreview
func (vk *VK) MessagesGetChatPreview(params Params) (response MessagesGetChatPreviewResponse, err error) {
err = vk.RequestUnmarshal("messages.getChatPreview", &response, params)
return
@ -227,7 +237,7 @@ type MessagesGetConversationMembersResponse struct {
// MessagesGetConversationMembers returns a list of IDs of users participating in a conversation.
//
// https://vk.com/dev/messages.getConversationMembers
// https://dev.vk.com/method/messages.getConversationMembers
func (vk *VK) MessagesGetConversationMembers(params Params) (
response MessagesGetConversationMembersResponse,
err error,
@ -246,7 +256,7 @@ type MessagesGetConversationsResponse struct {
// MessagesGetConversations returns a list of conversations.
//
// https://vk.com/dev/messages.getConversations
// https://dev.vk.com/method/messages.getConversations
func (vk *VK) MessagesGetConversations(params Params) (response MessagesGetConversationsResponse, err error) {
err = vk.RequestUnmarshal("messages.getConversations", &response, params)
return
@ -260,9 +270,9 @@ type MessagesGetConversationsByIDResponse struct {
// MessagesGetConversationsByID returns conversations by their IDs.
//
// extended=0
// extended=0
//
// https://vk.com/dev/messages.getConversationsById
// https://dev.vk.com/method/messages.getConversationsById
func (vk *VK) MessagesGetConversationsByID(params Params) (response MessagesGetConversationsByIDResponse, err error) {
err = vk.RequestUnmarshal("messages.getConversationsById", &response, params, Params{"extended": false})
@ -278,9 +288,9 @@ type MessagesGetConversationsByIDExtendedResponse struct {
// MessagesGetConversationsByIDExtended returns conversations by their IDs.
//
// extended=1
// extended=1
//
// https://vk.com/dev/messages.getConversationsById
// https://dev.vk.com/method/messages.getConversationsById
func (vk *VK) MessagesGetConversationsByIDExtended(params Params) (
response MessagesGetConversationsByIDExtendedResponse,
err error,
@ -309,7 +319,7 @@ type MessagesGetHistoryResponse struct {
// MessagesGetHistory returns message history for the specified user or group chat.
//
// https://vk.com/dev/messages.getHistory
// https://dev.vk.com/method/messages.getHistory
func (vk *VK) MessagesGetHistory(params Params) (response MessagesGetHistoryResponse, err error) {
err = vk.RequestUnmarshal("messages.getHistory", &response, params)
return
@ -324,7 +334,7 @@ type MessagesGetHistoryAttachmentsResponse struct {
// MessagesGetHistoryAttachments returns media files from the dialog or group chat.
//
// https://vk.com/dev/messages.getHistoryAttachments
// https://dev.vk.com/method/messages.getHistoryAttachments
func (vk *VK) MessagesGetHistoryAttachments(params Params) (response MessagesGetHistoryAttachmentsResponse, err error) {
err = vk.RequestUnmarshal("messages.getHistoryAttachments", &response, params)
return
@ -342,7 +352,7 @@ type MessagesGetImportantMessagesResponse struct {
// MessagesGetImportantMessages messages.getImportantMessages.
//
// https://vk.com/dev/messages.getImportantMessages
// https://dev.vk.com/method/messages.getImportantMessages
func (vk *VK) MessagesGetImportantMessages(params Params) (response MessagesGetImportantMessagesResponse, err error) {
err = vk.RequestUnmarshal("messages.getImportantMessages", &response, params)
return
@ -357,7 +367,7 @@ type MessagesGetIntentUsersResponse struct {
// MessagesGetIntentUsers method.
//
// https://vk.com/dev/messages.getIntentUsers
// https://dev.vk.com/method/messages.getIntentUsers
func (vk *VK) MessagesGetIntentUsers(params Params) (response MessagesGetIntentUsersResponse, err error) {
err = vk.RequestUnmarshal("messages.getIntentUsers", &response, params)
return
@ -370,7 +380,7 @@ type MessagesGetInviteLinkResponse struct {
// MessagesGetInviteLink receives a link to invite a user to the chat.
//
// https://vk.com/dev/messages.getInviteLink
// https://dev.vk.com/method/messages.getInviteLink
func (vk *VK) MessagesGetInviteLink(params Params) (response MessagesGetInviteLinkResponse, err error) {
err = vk.RequestUnmarshal("messages.getInviteLink", &response, params)
return
@ -381,7 +391,7 @@ type MessagesGetLastActivityResponse object.MessagesLastActivity
// MessagesGetLastActivity returns a user's current status and date of last activity.
//
// https://vk.com/dev/messages.getLastActivity
// https://dev.vk.com/method/messages.getLastActivity
func (vk *VK) MessagesGetLastActivity(params Params) (response MessagesGetLastActivityResponse, err error) {
err = vk.RequestUnmarshal("messages.getLastActivity", &response, params)
return
@ -405,7 +415,7 @@ type MessagesGetLongPollHistoryResponse struct {
// MessagesGetLongPollHistory returns updates in user's private messages.
//
// https://vk.com/dev/messages.getLongPollHistory
// https://dev.vk.com/method/messages.getLongPollHistory
func (vk *VK) MessagesGetLongPollHistory(params Params) (response MessagesGetLongPollHistoryResponse, err error) {
err = vk.RequestUnmarshal("messages.getLongPollHistory", &response, params)
return
@ -416,7 +426,7 @@ type MessagesGetLongPollServerResponse object.MessagesLongPollParams
// MessagesGetLongPollServer returns data required for connection to a Long Poll server.
//
// https://vk.com/dev/messages.getLongPollServer
// https://dev.vk.com/method/messages.getLongPollServer
func (vk *VK) MessagesGetLongPollServer(params Params) (response MessagesGetLongPollServerResponse, err error) {
err = vk.RequestUnmarshal("messages.getLongPollServer", &response, params)
return
@ -430,7 +440,7 @@ type MessagesIsMessagesFromGroupAllowedResponse struct {
// MessagesIsMessagesFromGroupAllowed returns information whether
// sending messages from the community to current user is allowed.
//
// https://vk.com/dev/messages.isMessagesFromGroupAllowed
// https://dev.vk.com/method/messages.isMessagesFromGroupAllowed
func (vk *VK) MessagesIsMessagesFromGroupAllowed(params Params) (
response MessagesIsMessagesFromGroupAllowedResponse,
err error,
@ -446,7 +456,7 @@ type MessagesJoinChatByInviteLinkResponse struct {
// MessagesJoinChatByInviteLink allows to enter the chat by the invitation link.
//
// https://vk.com/dev/messages.joinChatByInviteLink
// https://dev.vk.com/method/messages.joinChatByInviteLink
func (vk *VK) MessagesJoinChatByInviteLink(params Params) (response MessagesJoinChatByInviteLinkResponse, err error) {
err = vk.RequestUnmarshal("messages.joinChatByInviteLink", &response, params)
return
@ -454,7 +464,7 @@ func (vk *VK) MessagesJoinChatByInviteLink(params Params) (response MessagesJoin
// MessagesMarkAsAnsweredConversation messages.markAsAnsweredConversation.
//
// https://vk.com/dev/messages.markAsAnsweredConversation
// https://dev.vk.com/method/messages.markAsAnsweredConversation
func (vk *VK) MessagesMarkAsAnsweredConversation(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.markAsAnsweredConversation", &response, params)
return
@ -465,7 +475,7 @@ type MessagesMarkAsImportantResponse []int
// MessagesMarkAsImportant marks and un marks messages as important (starred).
//
// https://vk.com/dev/messages.markAsImportant
// https://dev.vk.com/method/messages.markAsImportant
func (vk *VK) MessagesMarkAsImportant(params Params) (response MessagesMarkAsImportantResponse, err error) {
err = vk.RequestUnmarshal("messages.markAsImportant", &response, params)
return
@ -473,7 +483,7 @@ func (vk *VK) MessagesMarkAsImportant(params Params) (response MessagesMarkAsImp
// MessagesMarkAsImportantConversation messages.markAsImportantConversation.
//
// https://vk.com/dev/messages.markAsImportantConversation
// https://dev.vk.com/method/messages.markAsImportantConversation
func (vk *VK) MessagesMarkAsImportantConversation(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.markAsImportantConversation", &response, params)
return
@ -481,7 +491,7 @@ func (vk *VK) MessagesMarkAsImportantConversation(params Params) (response int,
// MessagesMarkAsRead marks messages as read.
//
// https://vk.com/dev/messages.markAsRead
// https://dev.vk.com/method/messages.markAsRead
func (vk *VK) MessagesMarkAsRead(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.markAsRead", &response, params)
return
@ -492,7 +502,7 @@ type MessagesPinResponse object.MessagesMessage
// MessagesPin messages.pin.
//
// https://vk.com/dev/messages.pin
// https://dev.vk.com/method/messages.pin
func (vk *VK) MessagesPin(params Params) (response MessagesPinResponse, err error) {
err = vk.RequestUnmarshal("messages.pin", &response, params)
return
@ -502,7 +512,7 @@ func (vk *VK) MessagesPin(params Params) (response MessagesPinResponse, err erro
// current user started the chat, allows the user to remove another user from
// the chat.
//
// https://vk.com/dev/messages.removeChatUser
// https://dev.vk.com/method/messages.removeChatUser
func (vk *VK) MessagesRemoveChatUser(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.removeChatUser", &response, params)
return
@ -510,7 +520,7 @@ func (vk *VK) MessagesRemoveChatUser(params Params) (response int, err error) {
// MessagesRestore restores a deleted message.
//
// https://vk.com/dev/messages.restore
// https://dev.vk.com/method/messages.restore
func (vk *VK) MessagesRestore(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.restore", &response, params)
return
@ -526,7 +536,7 @@ type MessagesSearchResponse struct {
// MessagesSearch returns a list of the current user's private messages that match search criteria.
//
// https://vk.com/dev/messages.search
// https://dev.vk.com/method/messages.search
func (vk *VK) MessagesSearch(params Params) (response MessagesSearchResponse, err error) {
err = vk.RequestUnmarshal("messages.search", &response, params)
return
@ -541,7 +551,7 @@ type MessagesSearchConversationsResponse struct {
// MessagesSearchConversations returns a list of conversations that match search criteria.
//
// https://vk.com/dev/messages.searchConversations
// https://dev.vk.com/method/messages.searchConversations
func (vk *VK) MessagesSearchConversations(params Params) (response MessagesSearchConversationsResponse, err error) {
err = vk.RequestUnmarshal("messages.searchConversations", &response, params)
return
@ -551,7 +561,7 @@ func (vk *VK) MessagesSearchConversations(params Params) (response MessagesSearc
//
// For user_ids or peer_ids parameters, use MessagesSendUserIDs.
//
// https://vk.com/dev/messages.send
// https://dev.vk.com/method/messages.send
func (vk *VK) MessagesSend(params Params) (response int, err error) {
reqParams := Params{
"user_ids": "",
@ -575,9 +585,9 @@ type MessagesSendUserIDsResponse []struct {
// MessagesSendPeerIDs sends a message.
//
// need peer_ids;
// need peer_ids;
//
// https://vk.com/dev/messages.send
// https://dev.vk.com/method/messages.send
func (vk *VK) MessagesSendPeerIDs(params Params) (response MessagesSendUserIDsResponse, err error) {
err = vk.RequestUnmarshal("messages.send", &response, params)
return
@ -585,9 +595,9 @@ func (vk *VK) MessagesSendPeerIDs(params Params) (response MessagesSendUserIDsRe
// MessagesSendUserIDs sends a message.
//
// need user_ids or peer_ids;
// need user_ids or peer_ids;
//
// https://vk.com/dev/messages.send
// https://dev.vk.com/method/messages.send
//
// Deprecated: user_ids outdated, use MessagesSendPeerIDs.
func (vk *VK) MessagesSendUserIDs(params Params) (response MessagesSendUserIDsResponse, err error) {
@ -596,7 +606,7 @@ func (vk *VK) MessagesSendUserIDs(params Params) (response MessagesSendUserIDsRe
// MessagesSendMessageEventAnswer method.
//
// https://vk.com/dev/messages.sendMessageEventAnswer
// https://dev.vk.com/method/messages.sendMessageEventAnswer
func (vk *VK) MessagesSendMessageEventAnswer(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.sendMessageEventAnswer", &response, params)
return
@ -604,7 +614,7 @@ func (vk *VK) MessagesSendMessageEventAnswer(params Params) (response int, err e
// MessagesSendSticker sends a message.
//
// https://vk.com/dev/messages.sendSticker
// https://dev.vk.com/method/messages.sendSticker
func (vk *VK) MessagesSendSticker(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.sendSticker", &response, params, Params{"user_ids": ""})
@ -613,7 +623,7 @@ func (vk *VK) MessagesSendSticker(params Params) (response int, err error) {
// MessagesSetActivity changes the status of a user as typing in a conversation.
//
// https://vk.com/dev/messages.setActivity
// https://dev.vk.com/method/messages.setActivity
func (vk *VK) MessagesSetActivity(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.setActivity", &response, params)
return
@ -627,15 +637,31 @@ type MessagesSetChatPhotoResponse struct {
// MessagesSetChatPhoto sets a previously-uploaded picture as the cover picture of a chat.
//
// https://vk.com/dev/messages.setChatPhoto
// https://dev.vk.com/method/messages.setChatPhoto
func (vk *VK) MessagesSetChatPhoto(params Params) (response MessagesSetChatPhotoResponse, err error) {
err = vk.RequestUnmarshal("messages.setChatPhoto", &response, params)
return
}
// MessagesStartCallResponse struct.
type MessagesStartCallResponse struct {
JoinLink string `json:"join_link"`
CallID string `json:"call_id"`
}
// MessagesStartCall method.
//
// Deprecated: Use CallsStart
//
// https://dev.vk.com/method/messages.startCall
func (vk *VK) MessagesStartCall(params Params) (response MessagesStartCallResponse, err error) {
err = vk.RequestUnmarshal("messages.startCall", &response, params)
return
}
// MessagesUnpin messages.unpin.
//
// https://vk.com/dev/messages.unpin
// https://dev.vk.com/method/messages.unpin
func (vk *VK) MessagesUnpin(params Params) (response int, err error) {
err = vk.RequestUnmarshal("messages.unpin", &response, params)
return

View File

@ -7,7 +7,7 @@ import (
// NewsfeedAddBan prevents news from specified users and communities
// from appearing in the current user's newsfeed.
//
// https://vk.com/dev/newsfeed.addBan
// https://dev.vk.com/method/newsfeed.addBan
func (vk *VK) NewsfeedAddBan(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.addBan", &response, params)
return
@ -16,7 +16,7 @@ func (vk *VK) NewsfeedAddBan(params Params) (response int, err error) {
// NewsfeedDeleteBan allows news from previously banned users and
// communities to be shown in the current user's newsfeed.
//
// https://vk.com/dev/newsfeed.deleteBan
// https://dev.vk.com/method/newsfeed.deleteBan
func (vk *VK) NewsfeedDeleteBan(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.deleteBan", &response, params)
return
@ -24,7 +24,7 @@ func (vk *VK) NewsfeedDeleteBan(params Params) (response int, err error) {
// NewsfeedDeleteList the method allows you to delete a custom news list.
//
// https://vk.com/dev/newsfeed.deleteList
// https://dev.vk.com/method/newsfeed.deleteList
func (vk *VK) NewsfeedDeleteList(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.deleteList", &response, params)
return
@ -39,7 +39,7 @@ type NewsfeedGetResponse struct {
// NewsfeedGet returns data required to show newsfeed for the current user.
//
// https://vk.com/dev/newsfeed.get
// https://dev.vk.com/method/newsfeed.get
func (vk *VK) NewsfeedGet(params Params) (response NewsfeedGetResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.get", &response, params)
return
@ -53,9 +53,9 @@ type NewsfeedGetBannedResponse struct {
// NewsfeedGetBanned returns a list of users and communities banned from the current user's newsfeed.
//
// extended=0
// extended=0
//
// https://vk.com/dev/newsfeed.getBanned
// https://dev.vk.com/method/newsfeed.getBanned
func (vk *VK) NewsfeedGetBanned(params Params) (response NewsfeedGetBannedResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getBanned", &response, params, Params{"extended": false})
@ -69,9 +69,9 @@ type NewsfeedGetBannedExtendedResponse struct {
// NewsfeedGetBannedExtended returns a list of users and communities banned from the current user's newsfeed.
//
// extended=1
// extended=1
//
// https://vk.com/dev/newsfeed.getBanned
// https://dev.vk.com/method/newsfeed.getBanned
func (vk *VK) NewsfeedGetBannedExtended(params Params) (response NewsfeedGetBannedExtendedResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getBanned", &response, params, Params{"extended": true})
@ -87,7 +87,7 @@ type NewsfeedGetCommentsResponse struct {
// NewsfeedGetComments returns a list of comments in the current user's newsfeed.
//
// https://vk.com/dev/newsfeed.getComments
// https://dev.vk.com/method/newsfeed.getComments
func (vk *VK) NewsfeedGetComments(params Params) (response NewsfeedGetCommentsResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getComments", &response, params)
return
@ -106,7 +106,7 @@ type NewsfeedGetListsResponse struct {
// NewsfeedGetLists returns a list of newsfeeds followed by the current user.
//
// https://vk.com/dev/newsfeed.getLists
// https://dev.vk.com/method/newsfeed.getLists
func (vk *VK) NewsfeedGetLists(params Params) (response NewsfeedGetListsResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getLists", &response, params)
return
@ -120,7 +120,7 @@ type NewsfeedGetMentionsResponse struct {
// NewsfeedGetMentions returns a list of posts on user walls in which the current user is mentioned.
//
// https://vk.com/dev/newsfeed.getMentions
// https://dev.vk.com/method/newsfeed.getMentions
func (vk *VK) NewsfeedGetMentions(params Params) (response NewsfeedGetMentionsResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getMentions", &response, params)
return
@ -137,7 +137,7 @@ type NewsfeedGetRecommendedResponse struct {
// NewsfeedGetRecommended returns a list of newsfeeds recommended to the current user.
//
// https://vk.com/dev/newsfeed.getRecommended
// https://dev.vk.com/method/newsfeed.getRecommended
func (vk *VK) NewsfeedGetRecommended(params Params) (response NewsfeedGetRecommendedResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getRecommended", &response, params)
return
@ -151,7 +151,7 @@ type NewsfeedGetSuggestedSourcesResponse struct {
// NewsfeedGetSuggestedSources returns communities and users that current user is suggested to follow.
//
// https://vk.com/dev/newsfeed.getSuggestedSources
// https://dev.vk.com/method/newsfeed.getSuggestedSources
func (vk *VK) NewsfeedGetSuggestedSources(params Params) (response NewsfeedGetSuggestedSourcesResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.getSuggestedSources", &response, params)
return
@ -159,7 +159,7 @@ func (vk *VK) NewsfeedGetSuggestedSources(params Params) (response NewsfeedGetSu
// NewsfeedIgnoreItem hides an item from the newsfeed.
//
// https://vk.com/dev/newsfeed.ignoreItem
// https://dev.vk.com/method/newsfeed.ignoreItem
func (vk *VK) NewsfeedIgnoreItem(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.ignoreItem", &response, params)
return
@ -167,7 +167,7 @@ func (vk *VK) NewsfeedIgnoreItem(params Params) (response int, err error) {
// NewsfeedSaveList creates and edits user newsfeed lists.
//
// https://vk.com/dev/newsfeed.saveList
// https://dev.vk.com/method/newsfeed.saveList
func (vk *VK) NewsfeedSaveList(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.saveList", &response, params)
return
@ -183,9 +183,9 @@ type NewsfeedSearchResponse struct {
// NewsfeedSearch returns search results by statuses.
//
// extended=0
// extended=0
//
// https://vk.com/dev/newsfeed.search
// https://dev.vk.com/method/newsfeed.search
func (vk *VK) NewsfeedSearch(params Params) (response NewsfeedSearchResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.search", &response, params, Params{"extended": false})
@ -204,9 +204,9 @@ type NewsfeedSearchExtendedResponse struct {
// NewsfeedSearchExtended returns search results by statuses.
//
// extended=1
// extended=1
//
// https://vk.com/dev/newsfeed.search
// https://dev.vk.com/method/newsfeed.search
func (vk *VK) NewsfeedSearchExtended(params Params) (response NewsfeedSearchExtendedResponse, err error) {
err = vk.RequestUnmarshal("newsfeed.search", &response, params, Params{"extended": true})
@ -215,7 +215,7 @@ func (vk *VK) NewsfeedSearchExtended(params Params) (response NewsfeedSearchExte
// NewsfeedUnignoreItem returns a hidden item to the newsfeed.
//
// https://vk.com/dev/newsfeed.unignoreItem
// https://dev.vk.com/method/newsfeed.unignoreItem
func (vk *VK) NewsfeedUnignoreItem(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.unignoreItem", &response, params)
return
@ -223,7 +223,7 @@ func (vk *VK) NewsfeedUnignoreItem(params Params) (response int, err error) {
// NewsfeedUnsubscribe unsubscribes the current user from specified newsfeeds.
//
// https://vk.com/dev/newsfeed.unsubscribe
// https://dev.vk.com/method/newsfeed.unsubscribe
func (vk *VK) NewsfeedUnsubscribe(params Params) (response int, err error) {
err = vk.RequestUnmarshal("newsfeed.unsubscribe", &response, params)
return

View File

@ -6,7 +6,7 @@ import (
// NotesAdd creates a new note for the current user.
//
// https://vk.com/dev/notes.add
// https://dev.vk.com/method/notes.add
func (vk *VK) NotesAdd(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.add", &response, params)
return
@ -14,7 +14,7 @@ func (vk *VK) NotesAdd(params Params) (response int, err error) {
// NotesCreateComment adds a new comment on a note.
//
// https://vk.com/dev/notes.createComment
// https://dev.vk.com/method/notes.createComment
func (vk *VK) NotesCreateComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.createComment", &response, params)
return
@ -22,7 +22,7 @@ func (vk *VK) NotesCreateComment(params Params) (response int, err error) {
// NotesDelete deletes a note of the current user.
//
// https://vk.com/dev/notes.delete
// https://dev.vk.com/method/notes.delete
func (vk *VK) NotesDelete(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.delete", &response, params)
return
@ -30,7 +30,7 @@ func (vk *VK) NotesDelete(params Params) (response int, err error) {
// NotesDeleteComment deletes a comment on a note.
//
// https://vk.com/dev/notes.deleteComment
// https://dev.vk.com/method/notes.deleteComment
func (vk *VK) NotesDeleteComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.deleteComment", &response, params)
return
@ -38,7 +38,7 @@ func (vk *VK) NotesDeleteComment(params Params) (response int, err error) {
// NotesEdit edits a note of the current user.
//
// https://vk.com/dev/notes.edit
// https://dev.vk.com/method/notes.edit
func (vk *VK) NotesEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.edit", &response, params)
return
@ -46,7 +46,7 @@ func (vk *VK) NotesEdit(params Params) (response int, err error) {
// NotesEditComment edits a comment on a note.
//
// https://vk.com/dev/notes.editComment
// https://dev.vk.com/method/notes.editComment
func (vk *VK) NotesEditComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.editComment", &response, params)
return
@ -60,7 +60,7 @@ type NotesGetResponse struct {
// NotesGet returns a list of notes created by a user.
//
// https://vk.com/dev/notes.get
// https://dev.vk.com/method/notes.get
func (vk *VK) NotesGet(params Params) (response NotesGetResponse, err error) {
err = vk.RequestUnmarshal("notes.get", &response, params)
return
@ -71,7 +71,7 @@ type NotesGetByIDResponse object.NotesNote
// NotesGetByID returns a note by its ID.
//
// https://vk.com/dev/notes.getById
// https://dev.vk.com/method/notes.getById
func (vk *VK) NotesGetByID(params Params) (response NotesGetByIDResponse, err error) {
err = vk.RequestUnmarshal("notes.getById", &response, params)
return
@ -85,7 +85,7 @@ type NotesGetCommentsResponse struct {
// NotesGetComments returns a list of comments on a note.
//
// https://vk.com/dev/notes.getComments
// https://dev.vk.com/method/notes.getComments
func (vk *VK) NotesGetComments(params Params) (response NotesGetCommentsResponse, err error) {
err = vk.RequestUnmarshal("notes.getComments", &response, params)
return
@ -93,7 +93,7 @@ func (vk *VK) NotesGetComments(params Params) (response NotesGetCommentsResponse
// NotesRestoreComment restores a deleted comment on a note.
//
// https://vk.com/dev/notes.restoreComment
// https://dev.vk.com/method/notes.restoreComment
func (vk *VK) NotesRestoreComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notes.restoreComment", &response, params)
return

View File

@ -20,7 +20,7 @@ type NotificationsGetResponse struct {
// NotificationsGet returns a list of notifications about other users' feedback to the current user's wall posts.
//
// https://vk.com/dev/notifications.get
// https://dev.vk.com/method/notifications.get
func (vk *VK) NotificationsGet(params Params) (response NotificationsGetResponse, err error) {
err = vk.RequestUnmarshal("notifications.get", &response, params)
return
@ -29,7 +29,7 @@ func (vk *VK) NotificationsGet(params Params) (response NotificationsGetResponse
// NotificationsMarkAsViewed resets the counter of new notifications
// about other users' feedback to the current user's wall posts.
//
// https://vk.com/dev/notifications.markAsViewed
// https://dev.vk.com/method/notifications.markAsViewed
func (vk *VK) NotificationsMarkAsViewed(params Params) (response int, err error) {
err = vk.RequestUnmarshal("notifications.markAsViewed", &response, params)
return
@ -47,7 +47,7 @@ type NotificationsSendMessageResponse []struct {
// NotificationsSendMessage sends notification to the VK Apps user.
//
// https://vk.com/dev/notifications.sendMessage
// https://dev.vk.com/method/notifications.sendMessage
func (vk *VK) NotificationsSendMessage(params Params) (response NotificationsSendMessageResponse, err error) {
err = vk.RequestUnmarshal("notifications.sendMessage", &response, params)
return

View File

@ -6,7 +6,7 @@ import (
// OrdersCancelSubscription allows to cancel subscription.
//
// https://vk.com/dev/orders.cancelSubscription
// https://dev.vk.com/method/orders.cancelSubscription
func (vk *VK) OrdersCancelSubscription(params Params) (response int, err error) {
err = vk.RequestUnmarshal("orders.cancelSubscription", &response, params)
return
@ -17,7 +17,7 @@ type OrdersChangeStateResponse string // New state
// OrdersChangeState changes order status.
//
// https://vk.com/dev/orders.changeState
// https://dev.vk.com/method/orders.changeState
func (vk *VK) OrdersChangeState(params Params) (response OrdersChangeStateResponse, err error) {
err = vk.RequestUnmarshal("orders.changeState", &response, params)
return
@ -28,7 +28,7 @@ type OrdersGetResponse []object.OrdersOrder
// OrdersGet returns a list of orders.
//
// https://vk.com/dev/orders.get
// https://dev.vk.com/method/orders.get
func (vk *VK) OrdersGet(params Params) (response OrdersGetResponse, err error) {
err = vk.RequestUnmarshal("orders.get", &response, params)
return
@ -39,7 +39,7 @@ type OrdersGetAmountResponse []object.OrdersAmount
// OrdersGetAmount returns the cost of votes in the user's consent.
//
// https://vk.com/dev/orders.getAmount
// https://dev.vk.com/method/orders.getAmount
func (vk *VK) OrdersGetAmount(params Params) (response OrdersGetAmountResponse, err error) {
err = vk.RequestUnmarshal("orders.getAmount", &response, params)
return
@ -50,7 +50,7 @@ type OrdersGetByIDResponse []object.OrdersOrder
// OrdersGetByID returns information about orders by their IDs.
//
// https://vk.com/dev/orders.getByID
// https://dev.vk.com/method/orders.getByID
func (vk *VK) OrdersGetByID(params Params) (response OrdersGetByIDResponse, err error) {
err = vk.RequestUnmarshal("orders.getById", &response, params)
return
@ -61,7 +61,7 @@ type OrdersGetUserSubscriptionByIDResponse object.OrdersSubscription
// OrdersGetUserSubscriptionByID allows to get subscription by its ID.
//
// https://vk.com/dev/orders.getUserSubscriptionById
// https://dev.vk.com/method/orders.getUserSubscriptionById
func (vk *VK) OrdersGetUserSubscriptionByID(params Params) (response OrdersGetUserSubscriptionByIDResponse, err error) {
err = vk.RequestUnmarshal("orders.getUserSubscriptionById", &response, params)
return
@ -75,7 +75,7 @@ type OrdersGetUserSubscriptionsResponse struct {
// OrdersGetUserSubscriptions allows to get user's active subscriptions.
//
// https://vk.com/dev/orders.getUserSubscriptions
// https://dev.vk.com/method/orders.getUserSubscriptions
func (vk *VK) OrdersGetUserSubscriptions(params Params) (response OrdersGetUserSubscriptionsResponse, err error) {
err = vk.RequestUnmarshal("orders.getUserSubscriptions", &response, params)
return
@ -83,7 +83,7 @@ func (vk *VK) OrdersGetUserSubscriptions(params Params) (response OrdersGetUserS
// OrdersUpdateSubscription allows to update subscription price.
//
// https://vk.com/dev/orders.updateSubscription
// https://dev.vk.com/method/orders.updateSubscription
func (vk *VK) OrdersUpdateSubscription(params Params) (response int, err error) {
err = vk.RequestUnmarshal("orders.updateSubscription", &response, params)
return

Some files were not shown because too many files have changed in this diff Show More