Coder Social home page Coder Social logo

chanify / chanify Goto Github PK

View Code? Open in Web Editor NEW
1.2K 10.0 95.0 485 KB

Chanify is a safe and simple notification tools. This repository is command line tools for Chanify.

Home Page: https://www.chanify.net

License: MIT License

Dockerfile 0.11% Makefile 0.64% Go 98.00% Lua 0.48% JavaScript 0.77%
apns push-notifications golang server docker-image rest-api docker notifications-alert notification-service notification-api

chanify's Introduction

Chanify

Docker Release iTunes App Store WebStore Windows Workflow CodeQL Codecov Total alerts Go Report Card Go Reference GitHub Docker pull Downloads Users

English | 简体中文

Chanify is a safe and simple notification tools. For developers, system administrators, and everyone can push notifications with API.

Table of Contents

  1. Features
  2. Getting Started
  3. Installation
  4. Usage
  5. HTTP API
  6. Configuration
  7. Security
  8. Chrome Extension
  9. Windows Client
  10. Docker Compose
  11. Lua API
  12. Contributing
  13. License

Features

Chanify is include these features:

  • Customize channel for notifications.
  • Deploy your own node server.
  • Distributed architecture design.
  • Design for privacy protection.
  • Support text/image/audio/file message format.

Getting Started

  1. Install iOS App(1.0.0 version and above) or macOS App(1.3.0 version and above).
  2. Get send token, more detail.
  3. Send message.

Installation

Precompiled binary

Download precompiled binary from here.

Docker

$ docker pull wizjin/chanify:latest

From source

$ git clone https://github.com/chanify/chanify.git
$ cd chanify
$ make install

Usage

As Sender Client

Use chanify to send message.

# Text message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --text=<message>

# URL message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --link=<web url>

# Image message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --image=<image file path>

# Audio message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --audio=<audio file path>

# File message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --file=<file path> --text=<file description>

# Action  message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --text=<message> --title=<title> --action="<action name>|<action url>"

# Timeline message
$ chanify send --endpoint=http://<address>:<port> --token=<token> --timeline.code=<code> <item1>=<value1> <item2>=<value2> ...

endpoint default value is https://api.chanify.net, and notification will send by default server. If you have own node server, please set endpoint to your node server url.

As Serverless node

Chanify run in stateless mode, no device token will be stored in node.

All device token will be stored in api.chanify.net.

Message will send to apple apns server by api.chanify.net.

Send message workflow:

Start => node server => api.chanify.net => Apple server => iOS client
# Start chanify
$ chanify serve --host=<ip address> --port=<port> --secret=<secret key> --name=<node name> --endpoint=http://<address>:<port>

# Docker
$ docker run -it wizjin/chanify:latest serve --secret=<secret key> --name=<node name> --endpoint=http://<address>:<port>

As Serverful node

Chanify run in stateful mode, device token will be stored in node.

Message will send to apple apns server direct.

Send message workflow:

Start => node server => Apple server => iOS client

Start server

# Start chanify
$ chanify serve --host=<ip address> --port=<port> --name=<node name> --datapath=~/.chanify --endpoint=http://<address>:<port>

# Docker
$ docker run -it -v /my/data:/root/.chanify wizjin/chanify:latest serve --name=<node name> --endpoint=http://<address>:<port>

Use MySQL as a backend

--dburl=mysql://<user>:<password>@tcp(<ip address>:<port>)/<database name>?charset=utf8mb4&parseTime=true&loc=Local

Chanify will not create database.

Add New Node

  • Start node server
  • iOS client can scan QRCode(http://<address>:<port>/) to add node.

Send message

Command Line

# Text message
$ curl --form-string "text=hello" "http://<address>:<port>/v1/sender/<token>"

# Text file
$ cat message.txt | curl -H "Content-Type: text/plain" --data-binary @- "http://<address>:<port>/v1/sender/<token>"

Python 3

from urllib import request, parse

data = parse.urlencode({ 'text': 'hello' }).encode()
req = request.Request("http://<address>:<port>/v1/sender/<token>", data=data)
request.urlopen(req)

Ruby

require 'net/http'

uri = URI('http://<address>:<port>/v1/sender/<token>')
res = Net::HTTP.post_form(uri, 'text' => 'hello')
puts res.body

NodeJS

const https = require('https')
const querystring = require('querystring');

const data = querystring.stringify({ text: 'hello' })
const options = {
    hostname: '<address>:<port>',
    port: 80,
    path: '/v1/sender/<token>',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': data.length
        }
    }
    var req = https.request(options, (res) => {
    res.on('data', (d) => {
        process.stdout.write(d);
    });
});  
req.write(data);
req.end();

PHP

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL           => 'http://<address>:<port>/v1/sender/<token>',
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS    => [ 'text' => 'hello' ],
]);

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP API

Send Text

  • GET
http://<address>:<port>/v1/sender/<token>/<message>
  • POST
http://<address>:<port>/v1/sender/<token>

Content-Type:

  • text/plain: Body is text message
  • multipart/form-data: The block of data("text") is text message
  • application/x-www-form-urlencoded: text=<url encoded text message>
  • application/json; charset=utf-8: The fields are optional
{
    "token": "<token>",
    "title": "<message title>",
    "text": "<text message content>",
    "copy": "<copy text for text message>",
    "autocopy": 1,
    "sound": 1,
    "priority": 10,
    "interruptionlevel": 0,
    "actions": [
        "ActionName1|http://<action host>/<action1>",
        "ActionName2|http://<action host>/<action2>",
        ...
    ],
    "timeline": {
        "code": "<timeline code>",
        "timestamp": 1620000000000,
        "items": {
            "key1": "value1",
            "key2": "value2",
            ...
        }
    }
}

Additional params

Key Default Description
title None The title for notification message.
copy None The copy text for text notification.
autocopy 0 Enable autocopy text for text notification.
sound 0 1 enable sound, otherwise disable sound.
priority 10 10 normal, 5 lower level.
interruption-level active Interruption level for timing of a notification.
actions None Actions list.
timeline None Timeline object.

sound:

  • 1 enable default sound
  • sound code, e.g. "bell"

interruption-level:

  • active: Lights up screen and may play a sound.
  • passive: Does not light up screen or play sound.
  • time-sensitive: Lights up screen and may play a sound; May be presented during Do Not Disturb.

timestamp in milliseconds (timezone - UTC)

E.g.

http://<address>:<port>/v1/sender/<token>?sound=1&priority=10&title=hello&copy=123&autocopy=1

Overwrite Content-Type

http://<address>:<port>/v1/sender/<token>?content-type=<text|json>

Send Link

$ curl --form "link=@<web url>" "http://<address>:<port>/v1/sender/<token>"
{
    "link": "<web url>",
    "sound": 1,
    "priority": 10,
}

Send Image

Send image only support POST method used serverful node.

  • Content-Type: image/png OR image/jpeg
cat <jpeg image path> | curl -H "Content-Type: image/jpeg" --data-binary @- "http://<address>:<port>/v1/sender/<token>"
  • Content-Type: multipart/form-data
$ curl --form "image=@<jpeg image path>" "http://<address>:<port>/v1/sender/<token>"

Send Audio

Send mp3 audio only support POST method used serverful node.

  • Content-Type: audio/mpeg
cat <mp3 audio path> | curl -H "Content-Type: audio/mpeg" --data-binary @- "http://<address>:<port>/v1/sender/<token>"
  • Content-Type: multipart/form-data
$ curl --form "audio=@<mp3 audio path>" "http://<address>:<port>/v1/sender/<token>"

Send File

Send file only support POST method used serverful node.

  • Content-Type: multipart/form-data
$ curl --form "file=@<file path>" "http://<address>:<port>/v1/sender/<token>"

Send Actions

Send Actions (Up to 4 actions).

  • Content-Type: multipart/form-data
$ curl --form "action=ActionName1|http://<action host>/<action1>" "http://<address>:<port>/v1/sender/<token>"

URL for script action:

chanify://action/run-script/<script name>?<arg name 1>=<arg value 1>&<arg name 2>=<arg value 2>

Configuration

Chanify can be configured with a yml format file, and the default path is ~/.chanify.yml.

server:
    host: 0.0.0.0   # Listen ip address
    port: 8080      # Listen port
    endpoint: http://my.server/path # Endpoint URL
    name: Node name # Name for node server
    secret: <secret code> # key for serverless node server
    datapath: <data path> # data storage path for serverful node server
#   pluginpath: <plugin path> # plugin path for lua
    dburl: mysql://root:test@tcp(127.0.0.1:3306)/chanify?charset=utf8mb4&parseTime=true&loc=Local # database dsn for serverful node server
    http:
        - readtimeout: 10s  # 10 seconds for http read timeout
        - writetimeout: 10s # 10 seconds for http write timeout
    register:
        enable: false # Disable user register
        whitelist: # whitelist for user register
            - <user id 1>
            - <user id 2>
#   plugin:
#       webhook:
#           - name: github  # POST http://my.server/path/v1/webhook/github/<token>
#             file: webhook/github.lua # <pluginpath>/webhook/github.lua
#             env:
#               secret_token: "secret token"

client: # configuration for sender client
    sound: 1    # enable sound
    endpoint: <default node server endpoint>
    token: <default token>
    interruption-level: <interruption level>

Security

Setting Registrable

Node server can be disabled user registration and become a private server.

chanify serve --registerable=false --whitelist=<user1 id>,<user2 id>
  • --registerable=false: used to disable user registration
  • whitelist: list users can be add into node server

Token Lifetime

  • Token lifetime is about 90 days (default).
  • Can configurable token lifetime (1 day ~ 5 years) in channel detail page.

If your token is leaked, add leaked token into the blocklist (iOS client settings).

Note: Please protect your token from leakage. The blockist need trusted node server (1.1.9 version and above).

Chrome Extension

Download the extension from Chrome web store.

Extension features:

  • Send select text/image/audio/url message to Chanify
  • Send page url to Chanify

Windows Client

Get the Windows Client from here.

Windows Client features:

  • Support Chanify to the Windows Send To Menu.
  • Support send message with CLI.

Docker Compose

  1. Install docker compose.
  2. Edit configuration file (docker-compose.yml).
  3. Start docker compose: docker-compose up -d

docker-compose.yml:

version: "3"
services:
    web:
        image: nginx:alpine
        restart: always
        volumes:
            - <workdir>/nginx.conf:/etc/nginx/nginx.conf
            - <workdir>/ssl:/ssl
        ports:
            - 80:80
            - 443:443
    chanify:
        image: wizjin/chanify:dev
        restart: always
        volumes:
            - <workdir>/data:/data
            - <workdir>/chanify.yml:/root/.chanify.yml
Key Description
workdir Work directory for node server.

<workdir>/nginx.conf:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
	worker_connections  1024;
}

http {
	include       /etc/nginx/mime.types;
	default_type  application/octet-stream;
	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

	access_log  /var/log/nginx/access.log  main;

	server_tokens   off;
	autoindex       off;
	sendfile        on;
	tcp_nopush      on;
	tcp_nodelay     on;

	keepalive_timeout  10;

    server {
		listen				80;
		server_name         <hostname or ip>;
		access_log          off;
		return 301 https://$host$request_uri;
	}

	server {
		listen              443 ssl http2;
		server_name         <hostname or ip>;
		ssl_certificate     /ssl/<ssl key>.crt;
		ssl_certificate_key /ssl/<ssl key>.key;
		ssl_protocols       TLSv1.2 TLSv1.3;
		ssl_ciphers         HIGH:!aNULL:!MD5;
		keepalive_timeout   30;
		charset             UTF-8;
		access_log          off;

		location / {
			proxy_set_header   Host               $host;
			proxy_set_header   X-Real-IP          $remote_addr;
			proxy_set_header   X-Forwarded-Proto  $scheme;
			proxy_set_header   X-Forwarded-For    $proxy_add_x_forwarded_for;
			proxy_pass http://chanify:8080/;
		}
	}
}
Key Description
hostname or ip Internet hostname or ip for node server.
ssl key SSL key file for node server.

<workdir>/chanify.yml:

server:
    endpoint: https://<hostname or ip>
    host: 0.0.0.0
    port: 80
    name: <node name>
    datapath: /data
    register:
        enable: false
        whitelist: # whitelist    
            - <user id>
Key Description
hostname or ip Internet hostname or ip for node server.
node name Name for node server.
user id User ids for whitelist.

Lua API

Usage

local hex = require "hex"
local bytes = hex.decode(hex_string)
local text = hex.encode(bytes_data)

local json = require "json"
local obj = json.decode(json_string)
local str = json.encode(json_object)

local crypto = require "crypto"
local is_equal = crypto.equal(mac1, mac2)
local mac = crypto.hmac("sha1", key, message) -- Support md5 sha1 sha256

-- Http request
local req = ctx:request()
local token_string = req:token()
local http_url = req:url()
local body_string = req:body()
local query_value = req:query("key")
local header_value = req:header("key")

-- Send message
ctx:send({
    title = "message title", -- Optional
    text = "message body",
    sound = "sound or not",  -- Optional
    copy = "copy",           -- Optional
    autocopy = "autocopy",   -- Optional 
})

Example: Github webhook event

Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Change to dev Branch (git checkout dev)
  3. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  4. Commit your Changes (git commit -m 'Add some AmazingFeature')
  5. Push to the Branch (git push origin feature/AmazingFeature)
  6. Open a Pull Request (merge to chanify:dev branch)

License

Distributed under the MIT License. See LICENSE for more information.

chanify's People

Contributors

bigtan avatar guanguans avatar harlin97 avatar wizjin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chanify's Issues

需求建议:点击通知后打开App增加自动操作

例如:
设置自动复制的文字类型的通知,点击通知后,打开App,自动复制文字到剪贴板(目前是需要下滑或长按通知才能复制)
链接类型的通知,可通过参数设置自动打开,此时点击通知打开App后自动打开对应链接(包括 scheme 类型)

Init service failed: In serverless mode, secret is required

使用配置文件的方式,会一直提示标题这个错误,要怎么解决啊?

chanify serve --config=/opt/chanify.yml

server:
    host: 0.0.0.0
    port: 10000
    endpoint: https://xxx.xxx.fun
    name: apple

配置文件增加 serverless: false 也不行

怎么用nginx反代啊?

求解!谢谢

这么启动的
chanify serve --host=0.0.0.0 --port=10000 --name=apple --datapath=/opt/chanify --endpoint=https://xxx.xxx.fun

白名单有更方便的方式来批量添加吗?

现有的添加白名单方式,有点限制死了,假设用docker部署,如果我要添加白名单,就得干掉镜像,重启一个,每次修改和添加都很不方便.
是否可以通过配置文件来配置?

feat: 希望可以在客户端的消息显示页面增加对a标签的支持

功能相关性
在使用推送文本信息时,会推送一些需要实时处理的信息。

解决方案
希望可以考虑在显示页面,增加对a标签的支持,这样可以对一些实时信息进行交互。

相关考虑
目前使用的方案是推送两次,先推送文本信息进行提醒,然后推送一个请求处理的url,比较麻烦。

其他
相关界面模拟 希望作者大大可以考虑
t1

链接类型优化

1.支持通过 Safari 或 SVC 打开链接
2.支持打开 url scheme 类型的链接
3.点击通知消息可以直接打开链接

支持通过 Safari 打开也就自动支持了 url scheme,而且也不再需要自行实现 webview

复制内容与自动复制

需求建议:
1.支持下拉通知复制内容,如 Bark 中下拉通知复制全部内容,若存在 copy 参数则只复制 copy 参数值
2.自动复制,无需下拉消息进行操作

connect node server failed

./chanify serve --host=0.0.0.0 --port=15000 --name=xxx --datapath=~/.chanify
2021/03/21 22:36:01 Launching service...
2021/03/21 22:36:01 Open sqlite database: /root/.chanify/chanify.db
2021/03/21 22:36:01 Node server name: xxx, version: unknown-version, serverless: false, node-id: XXX
2021/03/21 22:36:01 Launch service 0.0.0.0:15000
2021/03/21 22:36:01 Node server endpoint: http://0.0.0.0:15000

iOS 扫描报错 "connect node server failed"

version 1.0.0

求助,Thanks,

feat: custom notification priority

The priority of the notification. APNs sets the notification default priority to 10.

Specify 10 to send the notification immediately.
Specify 5 to send the notification based on power considerations on the user’s device.

使用app添加节点失败

./chanify serve --endpoint https://xxx.xxx.cn --host 127.0.0.1 --port 8080 --name xxx --secret xxxxx

然后用的caddy把https://xxx.xxx.cn反代到公网上,扫描二维码,无法添加节点

自建服务器的问题定位

你好,我现在控制台能收到get和post发送的数据,但是手机一直没收到,用api.chanify.net的是可以秒收到的,现在该怎么定位中间的问题呢?谢谢。另外endpoint域名和ip都试过,防火墙已关,分别在两台机器上实验过,是不是随便一台机器都可以往苹果的服务器发请求?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.