-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Features: * Configuration utility: use `megaphone configure` to set up the tool * Post to all supported social networks * Post to X with media files * Post only to X: use `megaphone -x "text"` to post only to X * Post to Mastodon: use `megaphone -m "text"` to post only to Mastodon
- Loading branch information
Showing
35 changed files
with
1,849 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export MEGOPHONE_MASTODON_SERVER="https://mastodon.social" | ||
export MEGOPHONE_X_OAUTH_TOKEN="$(bws secret get $x_oauth_token | jq -r '.value' )" | ||
export MEGOPHONE_X_OAUTH_TOKEN_SECRET="$(bws secret get $x_oauth_token_secret | jq -r '.value' )" | ||
export MEGOPHONE_X_API_KEY="$(bws secret get $x_api_key | jq -r '.value' )" | ||
export MEGOPHONE_X_API_KEY_SECRET="$(bws secret get $x_api_key_secret | jq -r '.value' )" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Release | ||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: | ||
- 'main' | ||
|
||
jobs: | ||
tests: | ||
uses: coolapso/megophone/.github/workflows/test.yml@dev | ||
|
||
release: | ||
needs: tests | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-go@v5 | ||
with: | ||
go-version: '>=1.23' | ||
- uses: go-semantic-release/action@v1 | ||
with: | ||
allow-initial-development-versions: true | ||
hooks: goreleaser | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
name: Go | ||
|
||
on: | ||
workflow_call: | ||
workflow_dispatch: | ||
pull_request: | ||
push: | ||
branches: | ||
- '**' | ||
- '!main' | ||
|
||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Setup Go | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: '>=1.23' | ||
|
||
- name: Install dependencies | ||
run: go get . | ||
|
||
- name: golangci-lint | ||
uses: golangci/golangci-lint-action@v6 | ||
with: | ||
version: latest | ||
only-new-issues: true | ||
|
||
- name: Run tests | ||
run: go test -cover ./... | ||
|
||
- name: test build | ||
run: go build -o megophone |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# .goreleaser.yaml | ||
builds: | ||
- binary: megophone | ||
env: | ||
- CGO_ENABLED=0 | ||
aurs: | ||
- name: megophone-bin | ||
homepage: "https://github.com/coolapso/megophone" | ||
description: "post to multiple social networks simultaneously from your CLI" | ||
maintainers: | ||
- "coolapso <[email protected]>" | ||
|
||
license: "MIT" | ||
private_key: "{{ .Env.AUR_KEY }}" | ||
git_url: "ssh://[email protected]/megophone-bin.git" | ||
skip_upload: auto | ||
|
||
# Git author used to commit to the repository. | ||
commit_author: | ||
name: goreleaserbot | ||
email: [email protected] | ||
|
||
announce: | ||
discord: | ||
enabled: true | ||
|
||
mastodon: | ||
enabled: true | ||
server: https://mastodon.social | ||
|
||
twitter: | ||
enabled: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright © 2024 coolapso | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
build: | ||
go build -o megophone | ||
test: | ||
go test -cover ./... | ||
fmt: | ||
go fmt ./... | ||
lint: | ||
golangci-lint run ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,116 @@ | ||
# xm-cli | ||
# xm-cli | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/coolapso/megophone/refs/heads/dev/media/megophone.png" width="200" > | ||
</p> | ||
|
||
# megophone | ||
|
||
A single tool for multiple social networks. | ||
|
||
Megaphone allows you to post to multiple social networks simultaneously from your CLI. | ||
|
||
|
||
## Features | ||
|
||
* Configuration utility: use `megaphone configure` to set up the tool | ||
* Post to all supported social networks | ||
* Post to all supported social networkds with images and videos | ||
* Post only to X: use `megaphone -x "text"` to post only to X | ||
* Post to Mastodon: use `megaphone -m "text"` to post only to Mastodon | ||
|
||
|
||
### Planed features | ||
|
||
* Threads | ||
* Facebook (Still not very sure about this one) | ||
* Threading, split longer texts and post them as threads | ||
* Polls | ||
|
||
## Installation | ||
|
||
Easier ways to install are planned and coming soon. | ||
|
||
### Go Install | ||
|
||
#### Latest version | ||
|
||
`go install github.com/coolapso/megophone` | ||
|
||
#### Specific version | ||
|
||
`go install github.com/coolapso/[email protected]` | ||
|
||
### Manual install | ||
|
||
* Grab the binary from the [releases page](https://github.com/coolapso/megophone/releases). | ||
* Extract the binary | ||
* Execute it | ||
|
||
## Setup | ||
|
||
Megophone needs access to your API Keys and Access tokens. For that, Megophone provides a configuration utility, which you can start with `megophone configure`. However, there are some steps you may need to do first. Once Megophone is configured, a configuration file with the necessary tokens and secrets is saved in `$XDG_CONFIG_HOME/megophone/config.yaml`. | ||
|
||
### X.com | ||
|
||
* Create an X developer account at: https://developer.x.com/en | ||
* Create a new app "megophone" | ||
* Generate tokens; Megophone needs read and write permissions. The necessary tokens are: | ||
* API Key | ||
* API Key Secret | ||
* OAuth Token | ||
* OAuth Token Secret | ||
* Provide the tokens when requested by the `megophone configure` command | ||
|
||
These tokens can also be provided with the following environment variables: | ||
`MEGOPHONE_X_API_KEY`, `MEGOPHONE_X_API_KEY_SECRET`, `MEGOPHONE_X_OAUTH_TOKEN`, `MEGOPHONE_X_OAUTH_TOKEN_SECRET` | ||
|
||
> [!NOTE] | ||
> You are subject to Twitter API pricing and limits. Please make sure to check the X developer portal information: https://developer.x.com/en | ||
|
||
### Mastodon | ||
|
||
* Mastodon configuration is all done through `megaphone configure`. During the process, it will open your browser and request you to paste the authorization code. | ||
|
||
## Usage | ||
|
||
``` | ||
Post to multiple social networks from your CLI | ||
Usage: | ||
megophone [flags] | ||
megophone [command] | ||
Available Commands: | ||
completion Generate the autocompletion script for the specified shell | ||
configure Configures megophone | ||
help Help about any command | ||
Flags: | ||
--config string config file (default is $XDG_HOME_CONFIG/megophone/config.yaml) | ||
-h, --help help for megophone | ||
-m, --m-only Post to Mastodon Only | ||
-p, --media-path string Path of media to be uploaded | ||
-x, --x-only Post to X only | ||
Use "megophone [command] --help" for more information about a command. | ||
``` | ||
|
||
## Build | ||
|
||
### With makefile | ||
|
||
`make build` | ||
|
||
### Manually | ||
|
||
`go build -o megophone` | ||
|
||
# Contributions | ||
|
||
Improvements and suggestions are always welcome, feel free to check for any open issues, open a new Issue or Pull Request | ||
|
||
If you like this project and want to support / contribute in a different way you can always: | ||
|
||
<a href="https://www.buymeacoffee.com/coolapso" target="_blank"> | ||
<img src="https://cdn.buymeacoffee.com/buttons/default-yellow.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" /> | ||
</a> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package cmd | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
"fmt" | ||
"github.com/coolapso/megophone/internal/util" | ||
"net/url" | ||
"os" | ||
"strings" | ||
|
||
gomasto "github.com/mattn/go-mastodon" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
func configMastodonServer(reader *bufio.Reader, c *config) { | ||
if server, isSet := os.LookupEnv("MEGOPHONE_MASTODON_SERVER"); isSet { | ||
c.m.SetServer(server) | ||
} | ||
|
||
fmt.Printf("Mastodon Server (%v): ", c.m.GetServer()) | ||
GetServerInput, _ := reader.ReadString('\n') | ||
if cleanInput := strings.TrimSpace(GetServerInput); cleanInput != "" { | ||
c.m.SetServer(cleanInput) | ||
} | ||
viper.Set("mastodon_server", c.m.GetServer()) | ||
} | ||
|
||
func registerMastodonApp(ctx context.Context, c *config) (*gomasto.Application, error) { | ||
appConfig := &gomasto.AppConfig{ | ||
Server: c.m.GetServer(), | ||
ClientName: "megophone", | ||
Scopes: "read write follow", | ||
Website: "https://github.com/coolapso/megophone", | ||
RedirectURIs: redirectUri, | ||
} | ||
|
||
return gomasto.RegisterApp(ctx, appConfig) | ||
} | ||
|
||
func getMastodonUserAuthorizationCode(reader *bufio.Reader, app *gomasto.Application) (string, error) { | ||
u, err := url.Parse(app.AuthURI) | ||
if err != nil { | ||
return "", fmt.Errorf("Failed to parse url, %v\n", err) | ||
} | ||
|
||
//We don't care about the error here, if it doesn't work, user can always grab the link | ||
_ = util.OpenURL(u.String()) | ||
fmt.Printf("Check your browser and copy/paste the given authorization code,\nif your browser didn't open use the url below:\n") | ||
fmt.Printf("\n%s\n\n", u) | ||
fmt.Print("Paste the code here:") | ||
getAccessTokenInput, _ := reader.ReadString('\n') | ||
authorizationCode := strings.TrimSpace(getAccessTokenInput) | ||
|
||
return authorizationCode, nil | ||
} | ||
|
||
func getAccessToken(ctx context.Context, authorizationCode string) (string, error) { | ||
client := gomasto.NewClient(mastodonClientConfig()) | ||
|
||
if err := client.AuthenticateToken(ctx, authorizationCode, redirectUri); err != nil { | ||
return "", err | ||
} | ||
|
||
return client.Config.AccessToken, nil | ||
} | ||
|
||
func mastodonClientConfig() *gomasto.Config { | ||
return &gomasto.Config{ | ||
Server: viper.GetString("mastodon_server"), | ||
ClientID: viper.GetString("mastodon_client_id"), | ||
ClientSecret: viper.GetString("mastodon_client_secret"), | ||
AccessToken: viper.GetString("mastodon_access_token"), | ||
} | ||
} | ||
|
||
func configMastodon(ctx context.Context, reader *bufio.Reader, c *config) error { | ||
configMastodonServer(reader, c) | ||
app, err := registerMastodonApp(ctx, c) | ||
if err != nil { | ||
return fmt.Errorf("Failed to register mastodon application %v\n", err) | ||
} | ||
|
||
viper.Set("mastodon_client_id", app.ClientID) | ||
viper.Set("mastodon_client_secret", app.ClientSecret) | ||
|
||
code, err := getMastodonUserAuthorizationCode(reader, app) | ||
if err != nil { | ||
return fmt.Errorf("Failed to configure mastodon access token, %v\n", err) | ||
} | ||
|
||
accessToken, err := getAccessToken(ctx, code) | ||
if err != nil { | ||
return fmt.Errorf("Failed to get access token, %v\n", err) | ||
} | ||
viper.Set("mastodon_access_token", accessToken) | ||
|
||
return nil | ||
} |
Oops, something went wrong.