mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-10 05:02:36 +08:00
[v3] Add provider methods, provide default login window, update readme for oauth
plugin
This commit is contained in:
parent
1d562d3c27
commit
3f55ce6dfc
@ -41,15 +41,6 @@ func main() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
oAuthWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
|
||||||
Title: "Login",
|
|
||||||
Width: 600,
|
|
||||||
Height: 850,
|
|
||||||
Hidden: true,
|
|
||||||
DisableResize: true,
|
|
||||||
URL: "http://localhost:9876/auth/github",
|
|
||||||
})
|
|
||||||
|
|
||||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||||
Title: "OAuth Demo",
|
Title: "OAuth Demo",
|
||||||
DevToolsEnabled: true,
|
DevToolsEnabled: true,
|
||||||
@ -61,17 +52,8 @@ func main() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Custom event handling
|
|
||||||
app.Events.On(oauth.Success, func(e *application.WailsEvent) {
|
|
||||||
oAuthWindow.Hide()
|
|
||||||
})
|
|
||||||
|
|
||||||
app.Events.On(oauth.Error, func(e *application.WailsEvent) {
|
|
||||||
oAuthWindow.Hide()
|
|
||||||
})
|
|
||||||
app.Events.On("github-login", func(e *application.WailsEvent) {
|
app.Events.On("github-login", func(e *application.WailsEvent) {
|
||||||
oAuthPlugin.Start()
|
oAuthPlugin.Github()
|
||||||
oAuthWindow.Show()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
err := app.Run()
|
err := app.Run()
|
||||||
|
@ -1,10 +1,71 @@
|
|||||||
# oauth Plugin
|
# oauth Plugin
|
||||||
|
|
||||||
This plugin provides the ability to initiate an OAuth authentication flow.
|
This plugin provides the ability to initiate an OAuth authentication flow with a wide range of OAuth providers:
|
||||||
|
|
||||||
|
- Amazon
|
||||||
|
- Apple
|
||||||
|
- Auth0
|
||||||
|
- AzureAD
|
||||||
|
- BattleNet
|
||||||
|
- Bitbucket
|
||||||
|
- Box
|
||||||
|
- Dailymotion
|
||||||
|
- Deezer
|
||||||
|
- DigitalOcean
|
||||||
|
- Discord
|
||||||
|
- Dropbox
|
||||||
|
- EveOnline
|
||||||
|
- Facebook
|
||||||
|
- Fitbit
|
||||||
|
- Gitea
|
||||||
|
- Gitlab
|
||||||
|
- Github
|
||||||
|
- Google
|
||||||
|
- GooglePlus
|
||||||
|
- Heroku
|
||||||
|
- Intercom
|
||||||
|
- Instagram
|
||||||
|
- Kakao
|
||||||
|
- LastFM
|
||||||
|
- LinkedIn
|
||||||
|
- Line
|
||||||
|
- Mastodon
|
||||||
|
- Meetup
|
||||||
|
- MicrosoftOnline
|
||||||
|
- Naver
|
||||||
|
- NextCloud
|
||||||
|
- Okta
|
||||||
|
- Onedrive
|
||||||
|
- OpenIDConnect
|
||||||
|
- Patreon
|
||||||
|
- PayPal
|
||||||
|
- SalesForce
|
||||||
|
- SeaTalk
|
||||||
|
- Shopify
|
||||||
|
- Slack
|
||||||
|
- SoundCloud
|
||||||
|
- Spotify
|
||||||
|
- Steam
|
||||||
|
- Strava
|
||||||
|
- Stripe
|
||||||
|
- TikTok
|
||||||
|
- Twitter
|
||||||
|
- TwitterV2
|
||||||
|
- Typetalk
|
||||||
|
- Twitch
|
||||||
|
- Uber
|
||||||
|
- VK
|
||||||
|
- WeCom
|
||||||
|
- Wepay
|
||||||
|
- Xero
|
||||||
|
- Yahoo
|
||||||
|
- Yammer
|
||||||
|
- Yandex
|
||||||
|
- Zoom
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Add the plugin to the `Plugins` option in the Applications options:
|
Add the plugin to the `Plugins` option in the Applications options. This example we are using the github provider:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@ -34,20 +95,109 @@ func main() {
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The plugin takes a `Config` struct as a parameter. This struct has the following fields:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
|
||||||
|
// Address to bind the temporary webserver to
|
||||||
|
// Defaults to localhost:9876
|
||||||
|
Address string
|
||||||
|
|
||||||
|
// SessionSecret is the secret used to encrypt the session store.
|
||||||
|
SessionSecret string
|
||||||
|
|
||||||
|
// MaxAge is the maximum age of the session in seconds.
|
||||||
|
MaxAge int
|
||||||
|
|
||||||
|
// Providers is a list of goth providers to use.
|
||||||
|
Providers []goth.Provider
|
||||||
|
|
||||||
|
// WindowConfig is the configuration for the window that will be opened
|
||||||
|
// to perform the OAuth login.
|
||||||
|
WindowConfig *application.WebviewWindowOptions
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't specify a `WindowConfig`, the plugin will use the default window configuration:
|
||||||
|
|
||||||
|
```go
|
||||||
|
&application.WebviewWindowOptions{
|
||||||
|
Title: "OAuth Login",
|
||||||
|
Width: 600,
|
||||||
|
Height: 850,
|
||||||
|
Hidden: true,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Go
|
### Go
|
||||||
|
|
||||||
You can start the flow by calling `Start()` on the plugin instance:
|
You can start the flow by calling one of the provider methods:
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := oAuthPlugin.Github()
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we send an event from the frontend to start the process, so we listen for the event in the backend:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
app.Events.On("github-login", func(e *application.WailsEvent) {
|
app.Events.On("github-login", func(e *application.WailsEvent) {
|
||||||
oAuthPlugin.Start()
|
err := oAuthPlugin.Github()
|
||||||
oAuthWindow.Show()
|
if err != nil {
|
||||||
|
// process error
|
||||||
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
There is a working example of github auth in the `v3/examples` directory.
|
### JavaScript
|
||||||
|
|
||||||
|
You can start the flow by calling one of the provider methods:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
await wails.Plugin("oauth","Github")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handling Success & Failure
|
||||||
|
|
||||||
|
When the OAuth flow completes, the plugin will send one of 2 events:
|
||||||
|
|
||||||
|
- `wails:oauth:success` - The OAuth flow completed successfully. The event data will contain the user information.
|
||||||
|
- `wails:oauth:error` - The OAuth flow failed. The event data will contain the error message.
|
||||||
|
|
||||||
|
In Javascript, we can listen for these events like so:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
window.wails.Events.On("wails:oauth:success", (event) => {
|
||||||
|
document.getElementById("main").style.display = "none";
|
||||||
|
document.getElementById("name").innerText = event.data.Name;
|
||||||
|
document.getElementById("logo").src = event.data.AvatarURL;
|
||||||
|
document.body.style.backgroundColor = "#000";
|
||||||
|
document.body.style.color = "#FFF";
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to handle them in Go, you can do so like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
app.Events.On("wails:oauth:success", func(e *application.WailsEvent) {
|
||||||
|
// Do something with the user data
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Both these events are constants in the plugin:
|
||||||
|
|
||||||
|
```go
|
||||||
|
const (
|
||||||
|
Success = "wails:oauth:success"
|
||||||
|
Error = "wails:oauth:error"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a working example of GitHub auth in the `v3/examples/oauth` directory.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package oauth
|
package oauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/pat"
|
"github.com/gorilla/pat"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/markbates/goth"
|
"github.com/markbates/goth"
|
||||||
@ -40,6 +41,10 @@ type Config struct {
|
|||||||
|
|
||||||
// Providers is a list of goth providers to use.
|
// Providers is a list of goth providers to use.
|
||||||
Providers []goth.Provider
|
Providers []goth.Provider
|
||||||
|
|
||||||
|
// WindowConfig is the configuration for the window that will be opened
|
||||||
|
// to perform the OAuth login.
|
||||||
|
WindowConfig *application.WebviewWindowOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlugin(config Config) *Plugin {
|
func NewPlugin(config Config) *Plugin {
|
||||||
@ -52,6 +57,14 @@ func NewPlugin(config Config) *Plugin {
|
|||||||
if result.config.Address == "" {
|
if result.config.Address == "" {
|
||||||
result.config.Address = "localhost:9876"
|
result.config.Address = "localhost:9876"
|
||||||
}
|
}
|
||||||
|
if result.config.WindowConfig == nil {
|
||||||
|
result.config.WindowConfig = &application.WebviewWindowOptions{
|
||||||
|
Title: "OAuth Login",
|
||||||
|
Width: 600,
|
||||||
|
Height: 850,
|
||||||
|
Hidden: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +122,66 @@ func (p *Plugin) Init(_ *application.App) error {
|
|||||||
|
|
||||||
func (p *Plugin) CallableByJS() []string {
|
func (p *Plugin) CallableByJS() []string {
|
||||||
return []string{
|
return []string{
|
||||||
"Start",
|
"Amazon",
|
||||||
|
"Apple",
|
||||||
|
"Auth0",
|
||||||
|
"AzureAD",
|
||||||
|
"BattleNet",
|
||||||
|
"Bitbucket",
|
||||||
|
"Box",
|
||||||
|
"Dailymotion",
|
||||||
|
"Deezer",
|
||||||
|
"DigitalOcean",
|
||||||
|
"Discord",
|
||||||
|
"Dropbox",
|
||||||
|
"EveOnline",
|
||||||
|
"Facebook",
|
||||||
|
"Fitbit",
|
||||||
|
"Gitea",
|
||||||
|
"Gitlab",
|
||||||
|
"Github",
|
||||||
|
"Google",
|
||||||
|
"GooglePlus",
|
||||||
|
"Heroku",
|
||||||
|
"Intercom",
|
||||||
|
"Instagram",
|
||||||
|
"Kakao",
|
||||||
|
"LastFM",
|
||||||
|
"LinkedIn",
|
||||||
|
"Line",
|
||||||
|
"Mastodon",
|
||||||
|
"Meetup",
|
||||||
|
"MicrosoftOnline",
|
||||||
|
"Naver",
|
||||||
|
"NextCloud",
|
||||||
|
"Okta",
|
||||||
|
"Onedrive",
|
||||||
|
"OpenIDConnect",
|
||||||
|
"Patreon",
|
||||||
|
"PayPal",
|
||||||
|
"SalesForce",
|
||||||
|
"SeaTalk",
|
||||||
|
"Shopify",
|
||||||
|
"Slack",
|
||||||
|
"SoundCloud",
|
||||||
|
"Spotify",
|
||||||
|
"Steam",
|
||||||
|
"Strava",
|
||||||
|
"Stripe",
|
||||||
|
"TikTok",
|
||||||
|
"Twitter",
|
||||||
|
"TwitterV2",
|
||||||
|
"Typetalk",
|
||||||
|
"Twitch",
|
||||||
|
"Uber",
|
||||||
|
"VK",
|
||||||
|
"WeCom",
|
||||||
|
"Wepay",
|
||||||
|
"Xero",
|
||||||
|
"Yahoo",
|
||||||
|
"Yammer",
|
||||||
|
"Yandex",
|
||||||
|
"Zoom",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,19 +189,290 @@ func (p *Plugin) InjectJS() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- Plugin Methods ----------------
|
func (p *Plugin) start(provider string) error {
|
||||||
|
|
||||||
func (p *Plugin) Start() {
|
|
||||||
if p.server != nil {
|
if p.server != nil {
|
||||||
println("Already listening")
|
return fmt.Errorf("server already processing request. Please wait for the current login to complete")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
p.server = &http.Server{
|
p.server = &http.Server{
|
||||||
Addr: p.config.Address,
|
Addr: p.config.Address,
|
||||||
Handler: p.router,
|
Handler: p.router,
|
||||||
}
|
}
|
||||||
println("Starting server")
|
|
||||||
|
|
||||||
go p.server.ListenAndServe()
|
go p.server.ListenAndServe()
|
||||||
time.Sleep(1 * time.Second)
|
// Keep trying to connect until we succeed
|
||||||
|
var keepTrying = true
|
||||||
|
var connected = false
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
keepTrying = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
for keepTrying {
|
||||||
|
_, err := http.Get("http://" + p.config.Address)
|
||||||
|
if err == nil {
|
||||||
|
connected = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !connected {
|
||||||
|
return fmt.Errorf("server failed to start")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a window
|
||||||
|
p.config.WindowConfig.URL = "http://" + p.config.Address + "/auth/" + provider
|
||||||
|
window := application.Get().NewWebviewWindowWithOptions(*p.config.WindowConfig)
|
||||||
|
window.Show()
|
||||||
|
|
||||||
|
application.Get().Events.On(Success, func(event *application.WailsEvent) {
|
||||||
|
window.Close()
|
||||||
|
})
|
||||||
|
application.Get().Events.On(Error, func(event *application.WailsEvent) {
|
||||||
|
window.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- Plugin Methods ----------------
|
||||||
|
|
||||||
|
func (p *Plugin) Amazon() error {
|
||||||
|
return p.start("amazon")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Apple() error {
|
||||||
|
return p.start("apple")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Auth0() error {
|
||||||
|
return p.start("auth0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) AzureAD() error {
|
||||||
|
return p.start("azuread")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) BattleNet() error {
|
||||||
|
return p.start("battlenet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Bitbucket() error {
|
||||||
|
return p.start("bitbucket")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Box() error {
|
||||||
|
return p.start("box")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Dailymotion() error {
|
||||||
|
return p.start("dailymotion")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Deezer() error {
|
||||||
|
return p.start("deezer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) DigitalOcean() error {
|
||||||
|
return p.start("digitalocean")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Discord() error {
|
||||||
|
return p.start("discord")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Dropbox() error {
|
||||||
|
return p.start("dropbox")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) EveOnline() error {
|
||||||
|
return p.start("eveonline")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Facebook() error {
|
||||||
|
return p.start("facebook")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Fitbit() error {
|
||||||
|
return p.start("fitbit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Gitea() error {
|
||||||
|
return p.start("gitea")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Gitlab() error {
|
||||||
|
return p.start("gitlab")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Github() error {
|
||||||
|
return p.start("github")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Google() error {
|
||||||
|
return p.start("google")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) GooglePlus() error {
|
||||||
|
return p.start("gplus")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Heroku() error {
|
||||||
|
return p.start("heroku")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Intercom() error {
|
||||||
|
return p.start("intercom")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Instagram() error {
|
||||||
|
return p.start("instagram")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Kakao() error {
|
||||||
|
return p.start("kakao")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) LastFM() error {
|
||||||
|
return p.start("lastfm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) LinkedIn() error {
|
||||||
|
return p.start("linkedin")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Line() error {
|
||||||
|
return p.start("line")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Mastodon() error {
|
||||||
|
return p.start("mastodon")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Meetup() error {
|
||||||
|
return p.start("meetup")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) MicrosoftOnline() error {
|
||||||
|
return p.start("microsoftonline")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Naver() error {
|
||||||
|
return p.start("naver")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) NextCloud() error {
|
||||||
|
return p.start("nextcloud")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Okta() error {
|
||||||
|
return p.start("okta")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Onedrive() error {
|
||||||
|
return p.start("onedrive")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) OpenIDConnect() error {
|
||||||
|
return p.start("openid-connect")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Patreon() error {
|
||||||
|
return p.start("patreon")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) PayPal() error {
|
||||||
|
return p.start("paypal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) SalesForce() error {
|
||||||
|
return p.start("salesforce")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) SeaTalk() error {
|
||||||
|
return p.start("seatalk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Shopify() error {
|
||||||
|
return p.start("shopify")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Slack() error {
|
||||||
|
return p.start("slack")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) SoundCloud() error {
|
||||||
|
return p.start("soundcloud")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Spotify() error {
|
||||||
|
return p.start("spotify")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Steam() error {
|
||||||
|
return p.start("steam")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Strava() error {
|
||||||
|
return p.start("strava")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Stripe() error {
|
||||||
|
return p.start("stripe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) TikTok() error {
|
||||||
|
return p.start("tiktok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Twitter() error {
|
||||||
|
return p.start("twitter")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) TwitterV2() error {
|
||||||
|
return p.start("twitterv2")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Typetalk() error {
|
||||||
|
return p.start("typetalk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Twitch() error {
|
||||||
|
return p.start("twitch")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Uber() error {
|
||||||
|
return p.start("uber")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) VK() error {
|
||||||
|
return p.start("vk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) WeCom() error {
|
||||||
|
return p.start("wecom")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Wepay() error {
|
||||||
|
return p.start("wepay")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Xero() error {
|
||||||
|
return p.start("xero")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Yahoo() error {
|
||||||
|
return p.start("yahoo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Yammer() error {
|
||||||
|
return p.start("yammer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Yandex() error {
|
||||||
|
return p.start("yandex")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Zoom() error {
|
||||||
|
return p.start("zoom")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user