Initial commit
This commit is contained in:
commit
9e4063141b
|
@ -0,0 +1,25 @@
|
|||
GLWT(Good Luck With That) Public License
|
||||
Copyright (c) Everyone, except Author
|
||||
|
||||
Everyone is permitted to copy, distribute, modify, merge, sell, publish,
|
||||
sublicense or whatever they want with this software but at their OWN RISK.
|
||||
|
||||
Preamble
|
||||
|
||||
The author has absolutely no clue what the code in this project does.
|
||||
It might just work or not, there is no third option.
|
||||
|
||||
|
||||
GOOD LUCK WITH THAT PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION
|
||||
|
||||
0. You just DO WHATEVER YOU WANT TO as long as you NEVER LEAVE A
|
||||
TRACE TO TRACK THE AUTHOR of the original product to blame for or hold
|
||||
responsible.
|
||||
|
||||
IN NO EVENT SHALL THE AUTHORS 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.
|
||||
|
||||
Good luck and Godspeed.
|
|
@ -0,0 +1,31 @@
|
|||
CC ?= gcc
|
||||
CPPFLAGS ?= -D_FORTIFY_SOURCE=2
|
||||
CFLAGS ?= -std=gnu11 -Wall -Wextra -O2
|
||||
|
||||
-include Env.mak
|
||||
|
||||
override LDFLAGS += -ljson-c -luuid
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g
|
||||
LDFLAGS += -Wl,--strip-unneeded
|
||||
else
|
||||
CPPFLAGS += -DNDEBUG
|
||||
LDFLAGS += -Wl,--strip-all
|
||||
endif
|
||||
|
||||
__d = $(if $(value $1),-D$1='"$($1)"',$(info [WARN] $1 is undefined))
|
||||
|
||||
all: rpc run
|
||||
|
||||
rpc: CPPFLAGS += $(call __d,CLIENT_ID)
|
||||
rpc: CPPFLAGS += $(call __d,DETAILS)
|
||||
rpc: CPPFLAGS += $(call __d,STATE)
|
||||
rpc: CPPFLAGS += $(call __d,LARGE_IMAGE)
|
||||
rpc: CPPFLAGS += $(call __d,LARGE_TEXT)
|
||||
rpc: CPPFLAGS += $(call __d,SMALL_IMAGE)
|
||||
rpc: CPPFLAGS += $(call __d,SMALL_TEXT)
|
||||
rpc: rpc.c; @$(CC) $(CPPFLAGS) $(CFLAGS) $< $(LDFLAGS) -o $@
|
||||
|
||||
.PHONY: run
|
||||
run: rpc; @./$^
|
|
@ -0,0 +1,35 @@
|
|||
# discord-custom-rpc
|
||||
|
||||
Custom Discord Rich Presence for Linux written in C.
|
||||
|
||||
Depends on `json-c` & `uuid` and is configured during compilation.
|
||||
|
||||
First, create an [application][] and add some Rich Presence assets.
|
||||
|
||||
[application]: https://discord.com/developers/applications/
|
||||
|
||||
Then, create an `Env.mak` file to store the configuration.
|
||||
|
||||
```make
|
||||
# your client ID, required
|
||||
CLIENT_ID =
|
||||
# rich presence details, optional
|
||||
DETAILS =
|
||||
# rich presence state, optional
|
||||
STATE =
|
||||
# large image key, optional
|
||||
LARGE_IMAGE =
|
||||
# large image text, optional
|
||||
LARGE_TEXT =
|
||||
# small image key, optional
|
||||
SMALL_IMAGE =
|
||||
# small image text, optional
|
||||
SMALL_TEXT =
|
||||
# set to any value to enable debugging
|
||||
DEBUG =
|
||||
```
|
||||
|
||||
Now, you can compile and run the application with `make`.
|
||||
|
||||
Alternatively, use `make rpc` to only compile it.
|
||||
<br/>You can run the application later with `./rpc`
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __DEFS__
|
||||
#define __DEFS__
|
||||
|
||||
#ifndef CLIENT_ID
|
||||
#define CLIENT_ID NULL
|
||||
#error "CLIENT_ID must be defined"
|
||||
#endif // !CLIENT_ID
|
||||
|
||||
#ifndef DETAILS
|
||||
#define DETAILS NULL
|
||||
#endif // !DETAILS
|
||||
|
||||
#ifndef STATE
|
||||
#define STATE NULL
|
||||
#endif // !STATE
|
||||
|
||||
#ifndef LARGE_IMAGE
|
||||
#define LARGE_IMAGE NULL
|
||||
#endif // !LARGE_IMAGE
|
||||
|
||||
#ifndef LARGE_TEXT
|
||||
#define LARGE_TEXT NULL
|
||||
#endif // !LARGE_TEXT
|
||||
|
||||
#ifndef SMALL_IMAGE
|
||||
#define SMALL_IMAGE NULL
|
||||
#endif // !SMALL_IMAGE
|
||||
|
||||
#ifndef SMALL_TEXT
|
||||
#define SMALL_TEXT NULL
|
||||
#endif // !SMALL_TEXT
|
||||
|
||||
#endif // !__DEFS__
|
|
@ -0,0 +1,176 @@
|
|||
#include <json-c/json.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "utils.h"
|
||||
|
||||
typedef enum { OP_AUTHENTICATE, OP_FRAME, OP_CLOSE } rpc_op;
|
||||
|
||||
static int32_t sock = -1;
|
||||
|
||||
void rpc_connect() {
|
||||
struct sockaddr_un addr;
|
||||
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||
perror("[ERROR] socket failed");
|
||||
exit(1);
|
||||
}
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
const char *path = TMP_PATH;
|
||||
strncpy(addr.sun_path, path, strlen(path));
|
||||
strncat(addr.sun_path, "/discord-ipc-0", 15);
|
||||
printf("[INFO] connecting to %s\n", addr.sun_path);
|
||||
|
||||
if (connect(sock, (struct sockaddr *) &addr, sizeof addr) == -1) {
|
||||
perror("[ERROR] connect failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void rpc_send(rpc_op op, const char *data) {
|
||||
int32_t len = strlen(data);
|
||||
uint8_t op_b[4], len_b[4];
|
||||
INT_TO_BYTES(op, op_b);
|
||||
INT_TO_BYTES(len, len_b);
|
||||
|
||||
uint8_t *payload = (uint8_t *) malloc(8L + len);
|
||||
memcpy(payload, op_b, 4L);
|
||||
memcpy(payload + 4, len_b, 4L);
|
||||
memcpy(payload + 8, data, len);
|
||||
|
||||
LOG("[DEBUG] sending ");
|
||||
LOG_B(op_b);
|
||||
LOG_B(len_b);
|
||||
LOG("%s\n", data);
|
||||
if (send(sock, payload, 8L + len, 0) == -1) {
|
||||
free(payload);
|
||||
perror("[ERROR] send failed");
|
||||
exit(1);
|
||||
}
|
||||
free(payload);
|
||||
}
|
||||
|
||||
void rpc_recv(uint8_t *op, uint8_t *len, json_object **obj) {
|
||||
if (recv(sock, op, 4L, 0) == -1) {
|
||||
perror("[ERROR] recv failed");
|
||||
exit(1);
|
||||
}
|
||||
LOG("[DEBUG] received ");
|
||||
LOG_B(op);
|
||||
|
||||
if (recv(sock, len, 4L, 0) == -1) {
|
||||
fflush(stderr);
|
||||
perror("[ERROR] recv failed");
|
||||
exit(1);
|
||||
}
|
||||
uint32_t l = BYTES_TO_INT(len);
|
||||
LOG_B(len);
|
||||
|
||||
char *response = (char *) malloc(l);
|
||||
if (recv(sock, response, l, 0) == -1) {
|
||||
fflush(stderr);
|
||||
perror("[ERROR] recv failed");
|
||||
exit(1);
|
||||
}
|
||||
LOG("%s\n", response);
|
||||
|
||||
*obj = JSON_PARSE(response);
|
||||
free(response);
|
||||
}
|
||||
|
||||
void rpc_handshake() {
|
||||
json_object *r_obj, *s_obj = JSON_NEW();
|
||||
JSON_ADD(s_obj, v, 1, int);
|
||||
JSON_ADD(s_obj, client_id, CLIENT_ID, string);
|
||||
|
||||
rpc_send(OP_AUTHENTICATE, JSON_TO_STRING(s_obj));
|
||||
JSON_FREE(s_obj);
|
||||
|
||||
uint8_t op[4], len[4];
|
||||
rpc_recv(op, len, &r_obj);
|
||||
|
||||
const char *evt = JSON_GET(r_obj, evt, string);
|
||||
if (!STR_EQ(evt, "READY")) {
|
||||
const char *msg = JSON_GET(r_obj, message, string);
|
||||
JSON_FREE(r_obj);
|
||||
fprintf(stderr, "[ERROR] received evt: %s, message: %s\n", evt, msg);
|
||||
exit(2);
|
||||
}
|
||||
JSON_FREE(r_obj);
|
||||
}
|
||||
|
||||
void rpc_set_activity() {
|
||||
uint8_t op[4], len[4], uuid[16];
|
||||
|
||||
char *nonce = (char *) malloc(37L);
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse_upper(uuid, nonce);
|
||||
|
||||
json_object *r_obj, *s_obj = JSON_NEW();
|
||||
JSON_ADD(s_obj, cmd, "SET_ACTIVITY", string);
|
||||
JSON_ADD(s_obj, nonce, nonce, string);
|
||||
|
||||
json_object *activity = JSON_NEW();
|
||||
JSON_ADD_S(activity, details, DETAILS);
|
||||
JSON_ADD_S(activity, state, STATE);
|
||||
|
||||
json_object *timestamps = JSON_NEW();
|
||||
JSON_ADD(timestamps, start, TIME, int);
|
||||
JSON_ADD_OBJ(activity, timestamps, timestamps);
|
||||
|
||||
json_object *assets = JSON_NEW();
|
||||
JSON_ADD_S(assets, large_image, LARGE_IMAGE);
|
||||
JSON_ADD_S(assets, large_text, LARGE_TEXT);
|
||||
JSON_ADD_S(assets, small_image, SMALL_IMAGE);
|
||||
JSON_ADD_S(assets, small_text, SMALL_TEXT);
|
||||
JSON_ADD_OBJ(activity, assets, assets);
|
||||
|
||||
json_object *args = JSON_NEW();
|
||||
JSON_ADD(args, pid, PID, int);
|
||||
JSON_ADD_OBJ(args, activity, activity);
|
||||
JSON_ADD_OBJ(s_obj, args, args);
|
||||
|
||||
rpc_send(OP_FRAME, JSON_TO_STRING(s_obj));
|
||||
JSON_FREE(s_obj);
|
||||
free(nonce);
|
||||
|
||||
rpc_recv(op, len, &r_obj);
|
||||
const char *evt = JSON_GET(r_obj, evt, string);
|
||||
if (evt != NULL && STR_EQ(evt, "ERROR")) {
|
||||
json_object *data = JSON_GET_OBJ(r_obj, data);
|
||||
const char *msg = JSON_GET(data, message, string);
|
||||
JSON_FREE(r_obj);
|
||||
fprintf(stderr, "[ERROR] received message: %s\n", msg);
|
||||
exit(3);
|
||||
}
|
||||
JSON_FREE(r_obj);
|
||||
}
|
||||
|
||||
static void rpc_disconnect() {
|
||||
if (sock != -1) {
|
||||
rpc_send(OP_CLOSE, "");
|
||||
close(sock);
|
||||
}
|
||||
}
|
||||
|
||||
static void quit(__attribute__((unused)) int32_t sig) {
|
||||
fflush(stderr);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
atexit(rpc_disconnect);
|
||||
signal(SIGINT, quit);
|
||||
rpc_connect();
|
||||
rpc_handshake();
|
||||
rpc_set_activity();
|
||||
while (1) {}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef __UTILS__
|
||||
#define __UTILS__
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define LOG(msg, ...)
|
||||
#define LOG_B(bytes)
|
||||
#else
|
||||
#define LOG(msg, ...) fprintf(stderr, msg, ##__VA_ARGS__)
|
||||
#define LOG_B(bytes) \
|
||||
LOG("\\x%02x\\x%02x\\x%02x\\x%02x", /**/ \
|
||||
(bytes)[0], (bytes)[1], (bytes)[2], (bytes)[3]);
|
||||
#endif // NDEBUG
|
||||
|
||||
#define TIME ((int32_t) time(NULL))
|
||||
|
||||
#define PID ((int32_t) getpid())
|
||||
|
||||
#define TMP_PATH \
|
||||
( \
|
||||
getenv("XDG_RUNTIME_DIR") ?: \
|
||||
getenv("TMPDIR") ?: \
|
||||
getenv("TMP") ?: \
|
||||
getenv("TEMP") ?: \
|
||||
"/tmp" \
|
||||
)
|
||||
|
||||
#define STR_EQ(s1, s2) (strncmp((s1), (s2), strlen((s2))) == 0)
|
||||
|
||||
#define JSON_GET_OBJ(obj, key) json_object_object_get((obj), #key)
|
||||
|
||||
#define JSON_GET(obj, key, type) json_object_get_##type(JSON_GET_OBJ(obj, key))
|
||||
|
||||
#define JSON_ADD_OBJ(obj, key, val) json_object_object_add((obj), #key, (val))
|
||||
|
||||
#define JSON_ADD(obj, key, val, type) \
|
||||
JSON_ADD_OBJ(obj, key, json_object_new_##type((val)))
|
||||
|
||||
#define JSON_ADD_S(obj, key, val) \
|
||||
if ((val) != NULL) JSON_ADD(obj, key, val, string)
|
||||
|
||||
#define JSON_TO_STRING(obj) \
|
||||
json_object_to_json_string_ext((obj), JSON_C_TO_STRING_PLAIN)
|
||||
|
||||
#define JSON_PARSE(str) json_tokener_parse((str))
|
||||
|
||||
#define JSON_FREE(obj) json_object_put((obj))
|
||||
|
||||
#define JSON_NEW() json_object_new_object()
|
||||
|
||||
#define INT_TO_BYTES(n, bytes) \
|
||||
{ \
|
||||
(bytes)[0] = (n) & 0xFF; \
|
||||
(bytes)[1] = (n) >> 8 & 0xFF; \
|
||||
(bytes)[2] = (n) >> 16 & 0xFF; \
|
||||
(bytes)[3] = (n) >> 24 & 0xFF; \
|
||||
}
|
||||
|
||||
#define BYTES_TO_INT(bytes) \
|
||||
((bytes)[0] | (bytes)[1] << 8 | (bytes)[2] << 16 | (bytes)[3] << 24)
|
||||
|
||||
#endif // !__UTILS__
|
Loading…
Reference in New Issue