├── .clang-format ├── .github └── workflows │ └── buf.yml ├── .gitignore ├── CMakeLists.txt ├── FAQ.md ├── GUIDELINES.md ├── LICENSE ├── README.md ├── buf.work.yaml ├── shell.nix ├── stable ├── CMakeLists.txt ├── auth │ └── v1 │ │ └── auth.proto ├── batch │ └── v1 │ │ └── batch.proto ├── buf.yaml ├── chat │ └── v1 │ │ ├── Permissions.md │ │ ├── channels.proto │ │ ├── chat.proto │ │ ├── guilds.proto │ │ ├── messages.proto │ │ ├── permissions.proto │ │ └── stream.proto ├── emote │ └── v1 │ │ ├── emote.proto │ │ ├── stream.proto │ │ └── types.proto ├── harmonytypes │ └── v1 │ │ └── types.proto ├── mediaproxy │ └── v1 │ │ └── mediaproxy.proto ├── name-resolution │ ├── example │ │ ├── .gitignore │ │ ├── go.mod │ │ └── main.go │ └── name-resolution.md ├── profile │ └── v1 │ │ ├── appdata.proto │ │ ├── profile.proto │ │ ├── stream.proto │ │ └── types.proto ├── rest │ └── rest.md └── sync │ └── v1 │ └── sync.proto └── staging ├── CMakeLists.txt ├── bots └── v1 │ └── bots.proto ├── buf.yaml └── voice └── v1 └── voice.proto /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 0 3 | IndentWidth: 2 4 | -------------------------------------------------------------------------------- /.github/workflows/buf.yml: -------------------------------------------------------------------------------- 1 | name: Buf checks 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: 7 | - 'README.md' 8 | - 'rest/*' 9 | - 'name-resolution/*' 10 | - 'meson.build' 11 | pull_request: 12 | branches: [ main ] 13 | paths-ignore: 14 | - 'README.md' 15 | - 'rest/*' 16 | - 'name-resolution/*' 17 | - 'meson.build' 18 | 19 | jobs: 20 | lint-protos-stable: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: bufbuild/buf-setup-action@v0.5.0 25 | - uses: bufbuild/buf-lint-action@v1 26 | with: 27 | input: 'stable' 28 | lint-protos-staging: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v2 32 | - uses: bufbuild/buf-setup-action@v0.5.0 33 | - uses: bufbuild/buf-lint-action@v1 34 | with: 35 | input: 'staging' 36 | breaking-protos: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: bufbuild/buf-setup-action@v0.5.0 41 | - uses: bufbuild/buf-breaking-action@v1 42 | with: 43 | against: "https://github.com/harmony-development/protocol.git#branch=main" 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(HarmonyProtocols) 4 | 5 | include(GNUInstallDirs) 6 | 7 | option(USE_STAGING_PROTOCOLS "Whether or not to install staging protocols") 8 | 9 | add_subdirectory(stable) 10 | 11 | if (USE_STAGING_PROTOCOLS) 12 | add_subdirectory(staging) 13 | endif() 14 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Harmony FAQ 3 | --- 4 | 5 | ## What is Harmony? 6 | 7 | A chat protocol which: 8 | - is split into communities which contain channels which contain messages 9 | - is defined entirely within the [Protobuf](https://developers.google.com/protocol-buffers) format, using a simple [RPC mechanism called hRPC](https://github.com/harmony-development/hrpc). 10 | 11 | ## What are the design goals of Harmony? 12 | 13 | Harmony's protocol is designed to be as straightforward and pragmatic as possible. We do not make attempts at creating a "universal" design philosophy which the entire protocol is forced to follow, instead implementing things that make sense as a single cohesive system. Time has proven over time over that design idealism is often a limiting factor in services. 14 | 15 | ## Why not Matrix? 16 | 17 | - We believe that Matrix's design philosophy works in many cases, but fails in terms of performance and handling large communities. 18 | - We believe that requiring all data to be transferred between servers for federation is inefficient. 19 | - Harmony allows clients to connect directly to foreignservers if they wish for reduced latency and stress on their homeservers. 20 | - Harmony wishes to represent many things that don't work well in Matrix's model of "everything is a JSON message appended to a stream called a room." 21 | - Matrix is lacking in many functions we want from a chat service 22 | - Having an independent protocol allows us to move faster to achieve our goals compared to working on Matrix. 23 | 24 | ## Why not Revolt? 25 | 26 | Revolt is an interesting project, and we actually hadn't known about it while Harmony was under development. However, there are currently a few issues, namely: 27 | 28 | - No federation. The developers have stated that federation was too "messy" for a big chat app. We disagree. 29 | - It wants to be "Discord but open source", we want to be more than that. 30 | 31 | ## Where is E2EE? 32 | 33 | E2EE is currently under development as part of our "Secret Service" 34 | It is currently being worked on in a branch of our [Protocol](https://github.com/harmony-development/protocol/tree/work/e2ee) repository and a library called [Lockdown](https://github.com/harmony-development/lockdown), which allows clients to implement E2EE trivially. 35 | 36 | E2EE rooms adopt a different style from the rest of Harmony, allowing us to have all three by having a design specifically tailored to E2EE conversation: good performance, good security, good user experience. 37 | -------------------------------------------------------------------------------- /GUIDELINES.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Protocol Guidelines 3 | --- 4 | 5 | Protocol endpoints adhere to the following standard naming conventions: 6 | 7 | - All actions being applied must be **prefixed**. 8 | 9 | **❌ What not to do:** 10 | 11 | - `rpc ProfileUpdate(ProfileUpdateRequest) returns (ProfileUpdateResponse) {}` 12 | 13 | **✅ What to do:** 14 | 15 | - `rpc UpdateProfile(UpdateProfileRequest) returns (UpdateProfileResponse) {}` 16 | 17 | - Objects (types that aren't request or response types) should go at the top of the file. 18 | - Objects should not be nested inside request or response types. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Harmony Protocol 2 | 3 | This is the repository containing the Harmony protocol specification and documentation. 4 | 5 | Read [here](FAQ.md) for frequently asked questions. 6 | 7 | To read about protocol conventions, please read [GUIDELINES](GUIDELINES.md). 8 | 9 | If you are looking for: 10 | 11 | - a client to communicate with, check out [Tempest](https://github.com/harmony-development/tempest), [Challah](https://github.com/harmony-development/Challah) or [Crust](https://github.com/harmony-development/Crust). 12 | - a server to host, check out [Scherzo](https://github.com/harmony-development/scherzo). 13 | - an SDK to develop bots / clients / servers with, check out [Rust SDK](https://github.com/harmony-development/harmony_rust_sdk), [Web SDK](https://github.com/harmony-development/harmony-web-sdk) and [C++ SDK](https://github.com/harmony-development/Chometz). 14 | 15 | # Stable v. Staging 16 | 17 | Harmony has two types of protocol components: stable and staging. 18 | 19 | Stable protocols are long-lived protocols that are not expected to be 20 | replaced in the near future, and that have wide server and client support. 21 | 22 | Staging protocols are protocols that may be replaced in the near future, and they 23 | may not have wide server or client support. 24 | 25 | Both stable and staging protocols follow semantic versioning: breaking changes 26 | are only permitted with a corresponding increase in the major version, while 27 | non-breaking changes result in an increase in the minor version. 28 | 29 | Heavily in-dev protocols may be found on work branches in the Harmony repository, 30 | and are subject to no compatibility guarantees. They must have at least 1 server and 31 | 2 client implementations before becoming staging protocols. 32 | -------------------------------------------------------------------------------- /buf.work.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | directories: 3 | - stable 4 | - staging -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { }, ... }: 2 | pkgs.mkShell { 3 | nativeBuildInputs = with pkgs; [ buf protobuf ]; 4 | } 5 | -------------------------------------------------------------------------------- /stable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | foreach(dir IN ITEMS auth chat harmonytypes mediaproxy emote profile batch sync) 2 | install(DIRECTORY ${dir} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/harmony-protocols FILES_MATCHING PATTERN "*.proto") 3 | endforeach() 4 | -------------------------------------------------------------------------------- /stable/auth/v1/auth.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "harmonytypes/v1/types.proto"; 4 | 5 | package protocol.auth.v1; 6 | 7 | option go_package = "github.com/harmony-development/legato/gen/auth/v1"; 8 | 9 | // Used in `BeginAuth` endpoint. 10 | message BeginAuthRequest { 11 | // If provided, this is the guest token 12 | // for the account being upgraded from 13 | // a guest account to a full account 14 | // during the auth flow. 15 | // 16 | // This token is provided by the server 17 | // at the end of the auth flow that 18 | // created the guest account. 19 | optional string for_guest_token = 1; 20 | } 21 | 22 | // BeginAuthResponse 23 | // The return type of BeginAuth, containing the 24 | // auth_id that will be used for the authentication 25 | // section 26 | message BeginAuthResponse { 27 | // auth_id: the ID of this auth session 28 | string auth_id = 1; 29 | } 30 | 31 | // Session 32 | // Session contains the information for a new session; 33 | // the user_id you logged in as and the session_token 34 | // which should be passed to authorisation 35 | message Session { 36 | // user_id: the ID of the user you logged in as 37 | uint64 user_id = 1; 38 | // session_token: the session token to use in authorization 39 | string session_token = 2; 40 | // A token allowing for this account to be upgraded to a 41 | // "full" account by beginning an auth session and providing 42 | // this token. 43 | // 44 | // The guest token only exists to upgrade a guest account to a full 45 | // account, and does not permit logging in with a guest account 46 | // on more than one session. 47 | // 48 | // A "guest token" MAY be provided to BeginAuth to begin 49 | // an authorization process that will upgrade the guest account 50 | // to a full account if completed successfully. 51 | // This MUST only affect now being able to log 52 | // into the account with more than one session, and MUST not 53 | // change other information about the account, such as username 54 | // and password. 55 | optional string guest_token = 3; 56 | } 57 | 58 | // AuthStep 59 | // A step in the authentication process 60 | // Contains a variety of different types of views 61 | // It is recommended to have a fallback_url specified 62 | // For non-trivial authentication procedures (such as captchas) 63 | message AuthStep { 64 | // Choice 65 | // A step which allows the user to choose from a range of options 66 | // Allows you to show a heading by specifying title 67 | message Choice { 68 | // title: the title of the list of choices 69 | string title = 1; 70 | // options: a list of choices, one of these 71 | // should be sent in nextstep 72 | repeated string options = 2; 73 | } 74 | 75 | // Form 76 | // A step which requires the user to input information 77 | // Allows you to show a heading by specifying title 78 | message Form { 79 | // FormField 80 | // A field in the form, containing information on how it should 81 | // be rendered 82 | // Here is a list of form types that need to be supported: 83 | // email: a field type that has to contain a valid email 84 | // password: a field type that has to contain a password 85 | // new-password: a field type for new passwords 86 | // text: a field type that has to contain text 87 | // number: a field type that has to contain a number 88 | message FormField { 89 | // name: the identifier for the form field 90 | string name = 1; 91 | // type: the type of the form field, as documented above 92 | string type = 2; 93 | } 94 | 95 | // title: the title of this form 96 | string title = 1; 97 | 98 | // fields: all the fields in this form 99 | repeated FormField fields = 2; 100 | } 101 | 102 | // Waiting 103 | // A step which requires the user to perform an external action 104 | // The title and description should explain to the user 105 | // what they should do to complete this step 106 | message Waiting { 107 | // title: the title of this waiting screen 108 | string title = 1; 109 | // description: the explanation of what's being waited on 110 | string description = 2; 111 | } 112 | 113 | // fallback_url: unused 114 | string fallback_url = 1; 115 | // can_go_back: whether or not the client can request the 116 | // server to send the previous step 117 | bool can_go_back = 2; 118 | 119 | // step: the current step 120 | oneof step { 121 | // choice: the user must pick a thing out of a list of options 122 | Choice choice = 3; 123 | // form: the user must complete a form 124 | Form form = 4; 125 | // session: you've completed auth, and have a session 126 | Session session = 5; 127 | // waiting: you're waiting on something 128 | Waiting waiting = 6; 129 | } 130 | } 131 | 132 | // NextStepRequest 133 | // contains the client's response to the server's challenge 134 | // This needs to be called first with no arguments to 135 | // receive the first step 136 | message NextStepRequest { 137 | // auth_id: the authentication session you want 138 | // the next step of 139 | string auth_id = 1; 140 | 141 | // A simple choice string indicating which option the user chose 142 | message Choice { 143 | // choice: the choice the user picked 144 | string choice = 1; 145 | } 146 | 147 | // Form fields can either be bytes, string, or int64. 148 | message FormFields { 149 | // field: the data for a form field 150 | oneof field { 151 | // bytes: the form field's data is a byte array 152 | bytes bytes = 1; 153 | // string: the form field's data is a string 154 | string string = 2; 155 | // number: the form field's data is a number 156 | int64 number = 3; 157 | } 158 | } 159 | 160 | // An array of form fields, in the same order they came in from the server 161 | message Form { 162 | // fields: the fields the user filled out 163 | repeated FormFields fields = 1; 164 | } 165 | 166 | // step: the user's response to a step 167 | oneof step { 168 | // choice: the choice the user picked 169 | Choice choice = 2; 170 | // form: the form the user filled out 171 | Form form = 3; 172 | } 173 | } 174 | 175 | // Used in `NextStep` endpoint. 176 | message NextStepResponse { 177 | // step: the next step in the authentication process 178 | AuthStep step = 1; 179 | } 180 | 181 | // StepBackRequest 182 | // A request to go back 1 step 183 | message StepBackRequest { 184 | // auth_id: the authentication session the user 185 | // wants to go back in 186 | string auth_id = 1; 187 | } 188 | // Used in `StepBack` endpoint. 189 | message StepBackResponse { 190 | // step: the previous step in the authentication process 191 | AuthStep step = 1; 192 | } 193 | 194 | // StreamStepsRequest 195 | // Required to be initiated by all authenticating clients 196 | // Allows the server to send steps 197 | message StreamStepsRequest { 198 | // auth_id: the authorization session 199 | // who's steps you want to stream 200 | string auth_id = 1; 201 | } 202 | // Used in `StreamSteps` endpoint. 203 | message StreamStepsResponse { 204 | // step: the next step in the authentication process 205 | AuthStep step = 1; 206 | } 207 | 208 | // The request to federate with a foreign server. 209 | message FederateRequest { 210 | // The server ID foreign server you want to federate with 211 | string server_id = 1; 212 | } 213 | 214 | // The reply to a successful federation request, 215 | // containing the token you need to present to the 216 | // foreign server. 217 | message FederateResponse { 218 | // A `harmonytypes.v1.Token` whose `data` field is a serialized `TokenData` message. 219 | // It is signed with the homeserver's private key. 220 | harmonytypes.v1.Token token = 1; 221 | } 222 | 223 | // Used in `Key` endpoint. 224 | message KeyRequest {} 225 | 226 | // Contains a key's bytes. 227 | message KeyResponse { 228 | // key: the bytes of the public key. 229 | bytes key = 1; 230 | } 231 | 232 | // Log into a foreignserver using a token 233 | // from your homeserver, obtained through a FederateRequest 234 | message LoginFederatedRequest { 235 | // A `harmonytypes.v1.Token` whose `data` field is a serialized `TokenData` message. 236 | // It is signed with the homeserver's private key. 237 | harmonytypes.v1.Token auth_token = 1; 238 | // The server ID of the homeserver that the auth token is from 239 | string server_id = 2; 240 | } 241 | 242 | // Used in `LoginFederated` endpoint. 243 | message LoginFederatedResponse { 244 | // The user's session. 245 | Session session = 1; 246 | } 247 | 248 | // Information sent by a client's homeserver, in a `harmonytypes.v1.Token`. 249 | // It will be sent to a foreignserver by the client. 250 | message TokenData { 251 | // The client's user ID on the homeserver. 252 | uint64 user_id = 1; 253 | // The foreignserver's server ID. 254 | string server_id = 2; 255 | // The username of the client. 256 | string username = 3; 257 | // The avatar of the client. 258 | optional string avatar = 4; 259 | } 260 | 261 | // Used in `CheckLoggedIn` endpoint. 262 | message CheckLoggedInRequest {} 263 | // Used in `CheckLoggedIn` endpoint. 264 | message CheckLoggedInResponse {} 265 | 266 | // # Federation 267 | // 268 | // Servers should generate and persist an Ed25519 public and private 269 | // key. These will be referred to later on simply as the public 270 | // and private keys of the server. 271 | // 272 | // Federate is the core of Harmony's federation model. 273 | // The client sends the server name of the foreignserver 274 | // to its homeserver using the Federate method. 275 | // 276 | // The homeserver generates a `harmonytypes.v1.Token`, where the `data` field 277 | // contains a serialized `TokenData` message. 278 | // The private key used to sign is the homeserver's private key. 279 | // 280 | // The target should be the foreignserver's server name. 281 | // The user ID should be the client's user ID on the homeserver. 282 | // The username and avatar should be the client's username and avatar, 283 | // so the foreignserver knows what username and avatar to give them. 284 | // 285 | // The client will use this token in a LoginFederatedRequest request 286 | // and send it to the foreignserver as the auth_token field, with the 287 | // domain field filled out to the server name of the homeserver. 288 | // 289 | // TODO: finish 290 | 291 | // The service containing authorization/entication methods 292 | service AuthService { 293 | // Federate with a foreignserver, obtaining a token 294 | // you can use to call LoginFederated on it 295 | rpc Federate(FederateRequest) returns (FederateResponse); 296 | // Present a token to a foreignserver from a Federate call 297 | // on your homeserver in order to login 298 | rpc LoginFederated(LoginFederatedRequest) returns (LoginFederatedResponse); 299 | // Returns the public key of this server 300 | rpc Key(KeyRequest) returns (KeyResponse); 301 | // Begins an authentication session 302 | rpc BeginAuth(BeginAuthRequest) returns (BeginAuthResponse); 303 | // Goes to the next step of the authentication session, 304 | // possibly presenting user input 305 | rpc NextStep(NextStepRequest) returns (NextStepResponse); 306 | // Goes to the previous step of the authentication session 307 | // if possible 308 | rpc StepBack(StepBackRequest) returns (StepBackResponse); 309 | // Consume the steps of an authentication session 310 | // as a stream 311 | rpc StreamSteps(StreamStepsRequest) returns (stream StreamStepsResponse); 312 | // Check whether or not you're logged in and the session is valid 313 | rpc CheckLoggedIn(CheckLoggedInRequest) returns (CheckLoggedInResponse) { 314 | option (harmonytypes.v1.metadata).requires_authentication = true; 315 | option (harmonytypes.v1.metadata).requires_permission_node = ""; 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /stable/batch/v1/batch.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.batch.v1; 4 | 5 | // AnyRequest is a generic message supporting any unary request. 6 | message AnyRequest { 7 | // The endpoint to which the request is being sent. 8 | string endpoint = 1; 9 | // The request data. 10 | bytes request = 2; 11 | } 12 | 13 | // Used in `Batch` endpoint. 14 | message BatchRequest { 15 | // The list of requests to be executed in the batch. 16 | repeated AnyRequest requests = 1; 17 | } 18 | 19 | // Used in `Batch` endpoint. 20 | message BatchResponse { 21 | // The list of responses to the requests. 22 | repeated bytes responses = 1; 23 | } 24 | 25 | // Used in `BatchSame` endpoint. 26 | message BatchSameRequest { 27 | // The endpoint to call for all requests. 28 | string endpoint = 1; 29 | // The list of requests to be executed in the batch. 30 | repeated bytes requests = 2; 31 | } 32 | // Used in `BatchSame` endpoint. 33 | message BatchSameResponse { 34 | // The list of responses to the requests. 35 | repeated bytes responses = 1; 36 | } 37 | 38 | // Service to batch requests. 39 | service BatchService { 40 | // Batch requests. 41 | // Does not support batching stream requests. 42 | // Batched requests should be verified and an error should be thrown if they 43 | // are invalid. 44 | rpc Batch(BatchRequest) returns (BatchResponse) {} 45 | // BatchSame allows batching for requests using the same endpoint. 46 | // This allows for additional network optimizations since the endpoint doesn't 47 | // have to be sent for every request. 48 | rpc BatchSame(BatchSameRequest) returns (BatchSameResponse) {} 49 | } 50 | -------------------------------------------------------------------------------- /stable/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | name: harmonyapp.io/harmony-development/protocol 3 | lint: 4 | use: 5 | - DEFAULT 6 | - COMMENTS 7 | except: 8 | - PACKAGE_DIRECTORY_MATCH 9 | breaking: 10 | use: 11 | - FILE 12 | -------------------------------------------------------------------------------- /stable/chat/v1/Permissions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Permissions 3 | --- 4 | 5 | The permissions system of Harmony resembles that of the permissions system common 6 | to open-source Minecraft server software, featuring rules and querying a node string 7 | against those rules. 8 | 9 | ## Permission 10 | 11 | In a community, there will be features, management functionality, bot commands, and other things which exist. 12 | Most of these actions have a permission associated with them, allowing you to control which members have access to each feature. 13 | 14 | A permission is just a string, such as `roles.user.manage`, separated into parts using periods. 15 | 16 | This string is also referred to as a permission node, or just node for short. 17 | 18 | ## Rule 19 | 20 | A rule is a string that matches permissions, using UNIX-style globbing and parameter expansion. 21 | This matching string is paired with a mode, which will either allow or deny permissions that match the string. 22 | 23 | The rule `roles.*` will match `roles.user.manage` and `roles.user.view`. 24 | This is a star-expression, meaning that anything beginning with the text before the star and ending with the text after the star will match. 25 | Only one star-expression is permitted within a rule. 26 | 27 | The rule `roles.user.{manage,view}` will match `roles.user.manage` and `roles.user.view`, but not `roles.user.share`. 28 | This is an or-expression, meaning that `a.{b,c}` will match both `a.b` and `a.c`. 29 | Any amount of items can be put in the or-expression's braces: `a.{b,c,d}` will match both `a.b`, `a.c`, and `a.d`. 30 | Multiple or-expressions can compound, in which they effectively "multiply" the amount of items they expand to. 31 | 32 | `a.{b,c}.{d,e}` will expand to `a.b.d`, `a.b.e`, `a.c.d`, and `a.c.e`. 33 | 34 | ## Role 35 | 36 | A role is a collection of rules, which can be assigned to users. 37 | These are named and coloured for aesthetic reasons. 38 | 39 | Each guild MUST have a default role. This role MUST have the ID `0`. 40 | The default role will not be included in `GetGuildRoles` or `GetUserRoles`. 41 | Clients are free to show it however they want. 42 | 43 | ## Overrides 44 | 45 | A role can also have channel and category-specific overrides, which are a collection of rules that take precedence over the guild-level set of rules. 46 | Actions may query permissions in the context of a specific channel. 47 | For example, you may want to allow `@everyone` to `messages.send` in most channels, but deny them that permission to send messages in an announcements channel. 48 | 49 | Some actions may not query permissions in a channel or category, such as changing the name of a community. 50 | 51 | If an action queries in the context of a channel, it'll first check the overrides of the channel, then the rules of the category of the channel if applicable, then the rules associated with a guild. 52 | 53 | ## Evaluation 54 | 55 | ```go 56 | // Mode determines whether a permission will glob or not 57 | type Mode int 58 | 59 | // RoleID is the ID of a role 60 | type RoleID uint64 61 | 62 | // ChannelID is the ID of a channel 63 | type ChannelID uint64 64 | 65 | const ( 66 | // Allow permission 67 | Allow Mode = iota 68 | // Deny permission 69 | Deny 70 | ) 71 | 72 | type PermissionGlob struct { 73 | } 74 | 75 | func (p *PermissionGlob) Match(str string) bool { 76 | // return true if the string matches this glob, 77 | // otherwise false 78 | } 79 | 80 | type PermissionNode struct { 81 | Glob PermissionGlob 82 | Mode 83 | } 84 | 85 | type GuildState struct { 86 | // map of role IDs to list of permission nodes 87 | Roles map[RoleID][]PermissionNode 88 | 89 | // map of channel IDs to parent channel IDs (categories) 90 | Categories map[ChannelID]ChannelID 91 | 92 | // map of channel IDs to map of role IDs to list of permission nodes 93 | Channels map[ChannelID]map[RoleID][]PermissionNode 94 | } 95 | 96 | func (g GuildState) Check(permission string, userRoles []uint64, in ChannelID) bool { 97 | userRoles = append(userRoles, uint64(Everyone)) 98 | 99 | if in != 0 { 100 | if channelData, ok := g.Channels[in]; ok { 101 | for _, role := range userRoles { 102 | nodes, ok := channelData[RoleID(role)] 103 | _ = ok 104 | for _, node := range nodes { 105 | if node.Glob.Match(permission) { 106 | return node.Mode == Allow 107 | } 108 | } 109 | } 110 | } 111 | 112 | if category, ok := g.Categories[in]; ok { 113 | if channelData, ok := g.Channels[category]; ok { 114 | for _, role := range userRoles { 115 | nodes, ok := channelData[RoleID(role)] 116 | _ = ok 117 | for _, node := range nodes { 118 | if node.Glob.Match(permission) { 119 | return node.Mode == Allow 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | for _, role := range userRoles { 128 | nodes, ok := g.Roles[RoleID(role)] 129 | _ = ok 130 | for _, node := range nodes { 131 | if node.Glob.Match(permission) { 132 | return node.Mode == Allow 133 | } 134 | } 135 | } 136 | 137 | return false 138 | } 139 | ``` -------------------------------------------------------------------------------- /stable/chat/v1/channels.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "harmonytypes/v1/types.proto"; 4 | 5 | package protocol.chat.v1; 6 | 7 | option go_package = "github.com/harmony-development/legato/gen/chat/v1"; 8 | 9 | // What kind the channel is. 10 | enum ChannelKind { 11 | // A text channel. Allows you to simply send messages to a group of people. 12 | CHANNEL_KIND_TEXT_UNSPECIFIED = 0; 13 | // A voice channel. Allows you to talk to other people with voice. 14 | CHANNEL_KIND_VOICE_MEDIA = 1; 15 | // A category channel. All channels under this channel down to another 16 | // category channel belongs to this category channel. 17 | CHANNEL_KIND_CATEGORY = 2; 18 | } 19 | 20 | // An object representing a channel, without the ID. 21 | message Channel { 22 | // The name of this channel. 23 | string channel_name = 1; 24 | // The kind of channel this is. 25 | // Data does not get inherently stored in the Channel type 26 | // Instead, clients who understand a certain ChannelKind should 27 | // fetch them from a separate RPC. 28 | ChannelKind kind = 2; 29 | // The metadata of this channel. 30 | optional harmonytypes.v1.Metadata metadata = 3; 31 | } 32 | 33 | // The channel alongside with an ID. 34 | message ChannelWithId { 35 | // ID of the channel. 36 | uint64 channel_id = 1; 37 | // The channel data. 38 | Channel channel = 2; 39 | } 40 | 41 | // Channel Kinds: 42 | // 43 | // Channel kinds specified in an official Harmony protocol will start with a 44 | // "h." prefix. Third-party extensions should not use the "h." prefix. If no 45 | // kind is specified, the channel is a text channel. 46 | // 47 | // Kinds indicate additional functionality a channel may have: for example, 48 | // h.voice can indicate that a channel has voice functionalities alongside 49 | // the usual text fare. 50 | // 51 | // Used in the `CreateChannel` endpoint. 52 | message CreateChannelRequest { 53 | // Guild ID of the guild to create a channel in. 54 | uint64 guild_id = 1; 55 | // The name of this channel. 56 | string channel_name = 2; 57 | // The kind of this channel. 58 | ChannelKind kind = 3; 59 | // The metadata of this channel. 60 | optional harmonytypes.v1.Metadata metadata = 4; 61 | // The position of your new channel in the channel list. 62 | // 63 | // If not specified, it will be put at the bottom of the channel list. 64 | optional harmonytypes.v1.ItemPosition position = 5; 65 | } 66 | // Used in the `CreateChannel` endpoint. 67 | message CreateChannelResponse { 68 | // ID of the channel that was created. 69 | uint64 channel_id = 1; 70 | } 71 | 72 | // Used in the `GetGuildChannels` endpoint. 73 | message GetGuildChannelsRequest { 74 | // Guild ID of the guild you want to get channels from. 75 | uint64 guild_id = 1; 76 | } 77 | // Used in the `GetGuildChannels` endpoint. 78 | message GetGuildChannelsResponse { 79 | // Channels' data and ID the server responded with. 80 | repeated ChannelWithId channels = 1; 81 | } 82 | 83 | // Used in the `UpdateChannelInformation` endpoint. 84 | message UpdateChannelInformationRequest { 85 | // Guild ID of the guild where the channel is. 86 | uint64 guild_id = 1; 87 | // Channel ID of the channel you want to change the information of. 88 | uint64 channel_id = 2; 89 | // New name to set for this channel. 90 | optional string new_name = 3; 91 | // New metadata to set for this channel. 92 | optional harmonytypes.v1.Metadata new_metadata = 4; 93 | } 94 | // Used in the `UpdateChannelInformation` endpoint. 95 | message UpdateChannelInformationResponse {} 96 | 97 | // Used in the `UpdateChannelOrder` endpoint. 98 | message UpdateChannelOrderRequest { 99 | // Guild ID of the guild that has the channel. 100 | uint64 guild_id = 1; 101 | // Channel ID of the channel that you want to move. 102 | uint64 channel_id = 2; 103 | // The new position of this channel. 104 | harmonytypes.v1.ItemPosition new_position = 3; 105 | } 106 | // Used in the `UpdateChannelOrder` endpoint. 107 | message UpdateChannelOrderResponse {} 108 | 109 | // Request specifiying the order of all channels in a guild at once 110 | message UpdateAllChannelOrderRequest { 111 | // guild_id: the guild to specify the new channel order for 112 | uint64 guild_id = 1; 113 | // channel_ids: the new order of channel ids 114 | repeated uint64 channel_ids = 2; 115 | } 116 | // Used in the `UpdateAllChannelOrder` endpoint. 117 | message UpdateAllChannelOrderResponse {} 118 | 119 | // Used in the `DeleteChannel` endpoint. 120 | message DeleteChannelRequest { 121 | // Guild ID of the guild that has the channel. 122 | uint64 guild_id = 1; 123 | // Channel ID of the channel you want to delete. 124 | uint64 channel_id = 2; 125 | } 126 | // Used in the `DeleteChannel` endpoint. 127 | message DeleteChannelResponse {} 128 | 129 | // Used in `Typing` endpoint. 130 | message TypingRequest { 131 | // The guild id of the channel the user is typing in. 132 | uint64 guild_id = 1; 133 | // The channel id of the channel the user is typing in. 134 | uint64 channel_id = 2; 135 | } 136 | // Used in `Typing` endpoint. 137 | message TypingResponse {} -------------------------------------------------------------------------------- /stable/chat/v1/chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "harmonytypes/v1/types.proto"; 4 | import "chat/v1/guilds.proto"; 5 | import "chat/v1/channels.proto"; 6 | import "chat/v1/messages.proto"; 7 | import "chat/v1/permissions.proto"; 8 | import "chat/v1/stream.proto"; 9 | 10 | package protocol.chat.v1; 11 | 12 | option go_package = "github.com/harmony-development/legato/gen/chat/v1"; 13 | 14 | // The core of Harmony's chat operations. 15 | service ChatService { 16 | // Endpoint to create a guild. 17 | rpc CreateGuild(CreateGuildRequest) returns (CreateGuildResponse) { 18 | option (harmonytypes.v1.metadata).requires_authentication = true; 19 | } 20 | 21 | // Endpoint to create a "room" guild. 22 | rpc CreateRoom(CreateRoomRequest) returns (CreateRoomResponse) { 23 | option (harmonytypes.v1.metadata).requires_authentication = true; 24 | } 25 | 26 | // Endpoint to create a "direct message" guild. 27 | // 28 | // - The inviter and the invitee that join the created guild will both be owners. 29 | // - The guild should be created on the server that inviter is on. 30 | // - The server should send a guild invite to the invitee (specified in the request), 31 | // using the `UserInvited` postbox event if the invitee is on another server, 32 | // otherwise see the below item. 33 | // - The server should process this as follows: adding the invite to their pending 34 | // invite list and sending an `InviteReceived` event over their event stream if 35 | // the invitee is on this server. 36 | // - The invitee may or may not use the invite. Only the invitee may use the invite. 37 | rpc CreateDirectMessage(CreateDirectMessageRequest) returns (CreateDirectMessageResponse) { 38 | option (harmonytypes.v1.metadata).requires_authentication = true; 39 | } 40 | 41 | // Endpoint to upgrade a "room" guild to a "normal" guild. 42 | rpc UpgradeRoomToGuild(UpgradeRoomToGuildRequest) returns (UpgradeRoomToGuildResponse) { 43 | option (harmonytypes.v1.metadata).requires_authentication = true; 44 | option (harmonytypes.v1.metadata).requires_owner = true; 45 | } 46 | 47 | // Endpoint to create an invite. 48 | rpc CreateInvite(CreateInviteRequest) returns (CreateInviteResponse) { 49 | option (harmonytypes.v1.metadata).requires_authentication = true; 50 | option (harmonytypes.v1.metadata).requires_permission_node = 51 | "invites.manage.create"; 52 | } 53 | 54 | // Endpoint to create a channel. 55 | rpc CreateChannel(CreateChannelRequest) returns (CreateChannelResponse) { 56 | option (harmonytypes.v1.metadata).requires_authentication = true; 57 | option (harmonytypes.v1.metadata).requires_permission_node = 58 | "channels.manage.create"; 59 | } 60 | 61 | // Endpoint to get your guild list. 62 | rpc GetGuildList(GetGuildListRequest) returns (GetGuildListResponse) { 63 | option (harmonytypes.v1.metadata).requires_authentication = true; 64 | } 65 | 66 | // Endpoint to invite a user to a guild. 67 | rpc InviteUserToGuild(InviteUserToGuildRequest) returns (InviteUserToGuildResponse) { 68 | option (harmonytypes.v1.metadata).requires_authentication = true; 69 | option (harmonytypes.v1.metadata).requires_permission_node = 70 | "invites.manage.create"; 71 | } 72 | 73 | // Endpoint to get your pending invites. 74 | rpc GetPendingInvites(GetPendingInvitesRequest) returns (GetPendingInvitesResponse) { 75 | option (harmonytypes.v1.metadata).requires_authentication = true; 76 | } 77 | 78 | // Endpoint to reject (delete with notification to the inviter) an invite 79 | // from your pending invite list. 80 | // 81 | // If the invitee is on a different server than the inviter, the invitee's 82 | // server should send a `UserRejectedInvite` postbox event to the inviter's 83 | // server. 84 | // 85 | // The "notification" is sending a `InviteRejected` stream event to the 86 | // inviter. If the guild's kind is `DirectMessage`, the server should also 87 | // set the `rejected` field of the guild's kind to `true`. 88 | rpc RejectPendingInvite(RejectPendingInviteRequest) returns (RejectPendingInviteResponse) { 89 | option (harmonytypes.v1.metadata).requires_authentication = true; 90 | } 91 | 92 | // Endpoint to ignore (delete without notification to the inviter) an 93 | // invite from your pending invite list. 94 | rpc IgnorePendingInvite(IgnorePendingInviteRequest) returns (IgnorePendingInviteResponse) { 95 | option (harmonytypes.v1.metadata).requires_authentication = true; 96 | } 97 | 98 | // Endpoint to get information about a guild. 99 | rpc GetGuild(GetGuildRequest) returns (GetGuildResponse) { 100 | option (harmonytypes.v1.metadata).requires_authentication = true; 101 | } 102 | 103 | // Endpoint to get the invites of a guild. 104 | // 105 | // This requires the "invites.view" permission. 106 | rpc GetGuildInvites(GetGuildInvitesRequest) 107 | returns (GetGuildInvitesResponse) { 108 | option (harmonytypes.v1.metadata).requires_authentication = true; 109 | option (harmonytypes.v1.metadata).requires_permission_node = "invites.view"; 110 | } 111 | 112 | // Endpoint to get the members of a guild. 113 | rpc GetGuildMembers(GetGuildMembersRequest) 114 | returns (GetGuildMembersResponse) { 115 | option (harmonytypes.v1.metadata).requires_authentication = true; 116 | } 117 | 118 | // Endpoint to get the channels of a guild. 119 | // 120 | // You will only be informed of channels you have the "messages.view" 121 | // permission for. 122 | rpc GetGuildChannels(GetGuildChannelsRequest) 123 | returns (GetGuildChannelsResponse) { 124 | option (harmonytypes.v1.metadata).requires_authentication = true; 125 | } 126 | 127 | // Endpoint to get the messages from a guild channel. 128 | rpc GetChannelMessages(GetChannelMessagesRequest) 129 | returns (GetChannelMessagesResponse) { 130 | option (harmonytypes.v1.metadata).requires_authentication = true; 131 | option (harmonytypes.v1.metadata).requires_permission_node = 132 | "messages.view"; 133 | } 134 | 135 | // Endpoint to get a single message from a guild channel. 136 | rpc GetMessage(GetMessageRequest) returns (GetMessageResponse) { 137 | option (harmonytypes.v1.metadata).requires_authentication = true; 138 | option (harmonytypes.v1.metadata).requires_permission_node = 139 | "messages.view"; 140 | } 141 | 142 | // Endpoint to update a guild's information. 143 | rpc UpdateGuildInformation(UpdateGuildInformationRequest) 144 | returns (UpdateGuildInformationResponse) { 145 | option (harmonytypes.v1.metadata).requires_authentication = true; 146 | option (harmonytypes.v1.metadata).requires_permission_node = 147 | "guild.manage.change-information"; 148 | } 149 | 150 | // Endpoint to update a channel's information. 151 | rpc UpdateChannelInformation(UpdateChannelInformationRequest) 152 | returns (UpdateChannelInformationResponse) { 153 | option (harmonytypes.v1.metadata).requires_authentication = true; 154 | option (harmonytypes.v1.metadata).requires_permission_node = 155 | "channels.manage.change-information"; 156 | } 157 | 158 | // Endpoint to change the position of a channel in the channel list. 159 | rpc UpdateChannelOrder(UpdateChannelOrderRequest) 160 | returns (UpdateChannelOrderResponse) { 161 | option (harmonytypes.v1.metadata).requires_authentication = true; 162 | option (harmonytypes.v1.metadata).requires_permission_node = 163 | "channels.manage.move"; 164 | } 165 | 166 | // Endpoint to change the position of all channels in the guild; 167 | // must specifcy all channels or fails 168 | rpc UpdateAllChannelOrder(UpdateAllChannelOrderRequest) 169 | returns (UpdateAllChannelOrderResponse) { 170 | option (harmonytypes.v1.metadata).requires_authentication = true; 171 | option (harmonytypes.v1.metadata).requires_permission_node = 172 | "channels.manage.move"; 173 | } 174 | 175 | // Endpoint to change the text of a message. 176 | rpc UpdateMessageText(UpdateMessageTextRequest) returns (UpdateMessageTextResponse) { 177 | option (harmonytypes.v1.metadata).requires_authentication = true; 178 | option (harmonytypes.v1.metadata).requires_permission_node = 179 | "messages.send"; 180 | } 181 | 182 | // Endpoint to delete a guild. 183 | // Can only be invoked if there's one owner. 184 | rpc DeleteGuild(DeleteGuildRequest) returns (DeleteGuildResponse) { 185 | option (harmonytypes.v1.metadata).requires_authentication = true; 186 | option (harmonytypes.v1.metadata).requires_owner = true; 187 | } 188 | 189 | // Endpoint to delete an invite. 190 | rpc DeleteInvite(DeleteInviteRequest) returns (DeleteInviteResponse) { 191 | option (harmonytypes.v1.metadata).requires_authentication = true; 192 | option (harmonytypes.v1.metadata).requires_permission_node = 193 | "invites.manage.delete"; 194 | } 195 | 196 | // Endpoint to delete a channel. 197 | rpc DeleteChannel(DeleteChannelRequest) returns (DeleteChannelResponse) { 198 | option (harmonytypes.v1.metadata).requires_authentication = true; 199 | option (harmonytypes.v1.metadata).requires_permission_node = 200 | "channels.manage.delete"; 201 | } 202 | 203 | // Endpoint to delete a message. 204 | // 205 | // This requires the "messages.manage.delete" permission if you are not the 206 | // message author. 207 | rpc DeleteMessage(DeleteMessageRequest) returns (DeleteMessageResponse) { 208 | option (harmonytypes.v1.metadata).requires_authentication = true; 209 | } 210 | 211 | // Endpoint to join a guild. 212 | // 213 | // - If the invite used is in a user's "pending invites" list, it should be 214 | // removed from there. 215 | rpc JoinGuild(JoinGuildRequest) returns (JoinGuildResponse) { 216 | option (harmonytypes.v1.metadata).requires_authentication = true; 217 | } 218 | 219 | // Endpoint to leave a guild. 220 | // 221 | // - If you're the only owner, you can't leave a guild. Exception to this 222 | // rule are "direct message" guilds. 223 | // - When the last member in a "direct message" guild leaves it, that guild 224 | // should be deleted. 225 | rpc LeaveGuild(LeaveGuildRequest) returns (LeaveGuildResponse) { 226 | option (harmonytypes.v1.metadata).requires_authentication = true; 227 | } 228 | 229 | // Endpont to trigger an action. 230 | rpc TriggerAction(TriggerActionRequest) returns (TriggerActionResponse) { 231 | option (harmonytypes.v1.metadata).requires_authentication = true; 232 | option (harmonytypes.v1.metadata).requires_permission_node = 233 | "actions.trigger"; 234 | } 235 | 236 | // Endpoint to send a message. 237 | rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { 238 | option (harmonytypes.v1.metadata).requires_authentication = true; 239 | option (harmonytypes.v1.metadata).requires_permission_node = 240 | "messages.send"; 241 | } 242 | 243 | // Endpoint to query if a user has a permission. 244 | rpc QueryHasPermission(QueryHasPermissionRequest) returns (QueryHasPermissionResponse) { 245 | option (harmonytypes.v1.metadata).requires_authentication = true; 246 | // This permissions node is only required if you're trying 247 | // to see if someone besides yourself has a permission. 248 | // 249 | // option (harmonytypes.v1.metadata).requires_permission_node = 250 | // "permissions.query"; 251 | } 252 | 253 | // Endpoint to set permissions for a role in a guild. 254 | rpc SetPermissions(SetPermissionsRequest) returns (SetPermissionsResponse) { 255 | option (harmonytypes.v1.metadata).requires_authentication = true; 256 | option (harmonytypes.v1.metadata).requires_permission_node = 257 | "permissions.manage.set"; 258 | } 259 | 260 | // Endpoint to get permissions for a role in a guild. 261 | rpc GetPermissions(GetPermissionsRequest) returns (GetPermissionsResponse) { 262 | option (harmonytypes.v1.metadata).requires_authentication = true; 263 | option (harmonytypes.v1.metadata).requires_permission_node = 264 | "permissions.manage.get"; 265 | } 266 | 267 | // Endpoint to change the position of a role in the role list in a guild. 268 | rpc MoveRole(MoveRoleRequest) returns (MoveRoleResponse) { 269 | option (harmonytypes.v1.metadata).requires_authentication = true; 270 | option (harmonytypes.v1.metadata).requires_permission_node = "roles.manage"; 271 | } 272 | 273 | // Endpoint to get the roles from a guild. 274 | rpc GetGuildRoles(GetGuildRolesRequest) returns (GetGuildRolesResponse) { 275 | option (harmonytypes.v1.metadata).requires_authentication = true; 276 | option (harmonytypes.v1.metadata).requires_permission_node = "roles.get"; 277 | } 278 | 279 | // Endpoint to add a role to a guild. 280 | rpc AddGuildRole(AddGuildRoleRequest) returns (AddGuildRoleResponse) { 281 | option (harmonytypes.v1.metadata).requires_authentication = true; 282 | option (harmonytypes.v1.metadata).requires_permission_node = "roles.manage"; 283 | } 284 | 285 | // Endpoint to a modify a role from a guild. 286 | rpc ModifyGuildRole(ModifyGuildRoleRequest) returns (ModifyGuildRoleResponse) { 287 | option (harmonytypes.v1.metadata).requires_authentication = true; 288 | option (harmonytypes.v1.metadata).requires_permission_node = "roles.manage"; 289 | } 290 | 291 | // Endpoint to delete a role from a guild. 292 | rpc DeleteGuildRole(DeleteGuildRoleRequest) returns (DeleteGuildRoleResponse) { 293 | option (harmonytypes.v1.metadata).requires_authentication = true; 294 | option (harmonytypes.v1.metadata).requires_permission_node = "roles.manage"; 295 | } 296 | 297 | // Endpoint to manage the roles of a guild member. 298 | rpc ManageUserRoles(ManageUserRolesRequest) returns (ManageUserRolesResponse) { 299 | option (harmonytypes.v1.metadata).requires_authentication = true; 300 | option (harmonytypes.v1.metadata).requires_permission_node = 301 | "roles.user.manage"; 302 | } 303 | 304 | // Endpoint to get the roles a guild member has. 305 | rpc GetUserRoles(GetUserRolesRequest) returns (GetUserRolesResponse) { 306 | option (harmonytypes.v1.metadata).requires_authentication = true; 307 | // This permissions node is only required if you are trying to get the roles 308 | // of someone other than yourself. 309 | // 310 | // option (harmonytypes.v1.metadata).requires_permission_node = 311 | // "roles.user.get"; 312 | } 313 | 314 | // Endpoint to send a typing notification in a guild channel. 315 | rpc Typing(TypingRequest) returns (TypingResponse) { 316 | option (harmonytypes.v1.metadata).requires_authentication = true; 317 | option (harmonytypes.v1.metadata).requires_permission_node = 318 | "messages.send"; 319 | } 320 | 321 | // Endpoint to "preview" a guild, which returns some information about a 322 | // guild. 323 | // 324 | // - Guilds with the "direct message" kind can only be previewed 325 | // by the user who is invited to the guild. 326 | rpc PreviewGuild(PreviewGuildRequest) returns (PreviewGuildResponse) { 327 | option (harmonytypes.v1.metadata).requires_authentication = false; 328 | } 329 | 330 | // Endpoint to get banned users in a guild. 331 | rpc GetBannedUsers(GetBannedUsersRequest) returns (GetBannedUsersResponse) { 332 | option (harmonytypes.v1.metadata).requires_authentication = true; 333 | option (harmonytypes.v1.metadata).requires_permission_node = 334 | "guild.manage.change-information"; 335 | } 336 | 337 | // Endpoint to ban a user from a guild. 338 | rpc BanUser(BanUserRequest) returns (BanUserResponse) { 339 | option (harmonytypes.v1.metadata).requires_authentication = true; 340 | option (harmonytypes.v1.metadata).requires_permission_node = 341 | "user.manage.ban"; 342 | } 343 | 344 | // Endpoint to kick a user from a guild. 345 | rpc KickUser(KickUserRequest) returns (KickUserResponse) { 346 | option (harmonytypes.v1.metadata).requires_authentication = true; 347 | option (harmonytypes.v1.metadata).requires_permission_node = 348 | "user.manage.kick"; 349 | } 350 | 351 | // Endpoint to unban a user from a guild. 352 | rpc UnbanUser(UnbanUserRequest) returns (UnbanUserResponse) { 353 | option (harmonytypes.v1.metadata).requires_authentication = true; 354 | option (harmonytypes.v1.metadata).requires_permission_node = 355 | "user.manage.unban"; 356 | } 357 | 358 | // Endpoint to get all pinned messages in a guild channel. 359 | rpc GetPinnedMessages(GetPinnedMessagesRequest) 360 | returns (GetPinnedMessagesResponse) { 361 | option (harmonytypes.v1.metadata).requires_authentication = true; 362 | option (harmonytypes.v1.metadata).requires_permission_node = 363 | "messages.view"; 364 | } 365 | 366 | // Endpoint to pin a message in a guild channel. 367 | rpc PinMessage(PinMessageRequest) returns (PinMessageResponse) { 368 | option (harmonytypes.v1.metadata).requires_authentication = true; 369 | option (harmonytypes.v1.metadata).requires_permission_node = 370 | "messages.pins.add"; 371 | } 372 | 373 | // Endpoint to unpin a message in a guild channel. 374 | rpc UnpinMessage(UnpinMessageRequest) returns (UnpinMessageResponse) { 375 | option (harmonytypes.v1.metadata).requires_authentication = true; 376 | option (harmonytypes.v1.metadata).requires_permission_node = 377 | "messages.pins.remove"; 378 | } 379 | 380 | // Endpoint to stream events from the homeserver. 381 | // By default, this endpoint will subscribe to all events. 382 | // Any guilds joined in the future will be added to the subscription as well. 383 | // Use the UnsubscribeFromAll event for unsubscribing from all current subscriptions and disable the automatic guild subscriptions 384 | rpc StreamEvents(stream StreamEventsRequest) returns (stream StreamEventsResponse) { 385 | option (harmonytypes.v1.metadata).requires_authentication = true; 386 | } 387 | 388 | // Endpoint to add a reaction to a message. 389 | rpc AddReaction(AddReactionRequest) returns (AddReactionResponse) { 390 | option (harmonytypes.v1.metadata).requires_authentication = true; 391 | option (harmonytypes.v1.metadata).requires_permission_node = 392 | "messages.reactions.add"; 393 | } 394 | 395 | // Endpoint to remove a reaction from a message. 396 | rpc RemoveReaction(RemoveReactionRequest) returns (RemoveReactionResponse) { 397 | option (harmonytypes.v1.metadata).requires_authentication = true; 398 | option (harmonytypes.v1.metadata).requires_permission_node = 399 | "messages.reactions.remove"; 400 | } 401 | 402 | /// OWNERS 403 | 404 | // Endpoint to give ownership to someone else. 405 | rpc GrantOwnership(GrantOwnershipRequest) returns (GrantOwnershipResponse) { 406 | option (harmonytypes.v1.metadata).requires_authentication = true; 407 | option (harmonytypes.v1.metadata).requires_owner = true; 408 | } 409 | // Endpoint to give up your own ownership. 410 | // Requires that at least one other person will still be owner. 411 | rpc GiveUpOwnership(GiveUpOwnershipRequest) returns (GiveUpOwnershipResponse) { 412 | option (harmonytypes.v1.metadata).requires_authentication = true; 413 | option (harmonytypes.v1.metadata).requires_owner = true; 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /stable/chat/v1/guilds.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.chat.v1; 4 | 5 | import "harmonytypes/v1/types.proto"; 6 | 7 | option go_package = "github.com/harmony-development/legato/gen/chat/v1"; 8 | 9 | // The kind of a guild. 10 | message GuildKind { 11 | // A "normal" guild as in a guild that allows multiple channels. 12 | message Normal {} 13 | // A "room" guild as in a guild that only has one channel. 14 | // 15 | // - Clients should not show a channel list for guilds of this type. 16 | message Room {} 17 | // A "direct message" guild as in a guild that has at most two members, 18 | // and has only one channel. 19 | // 20 | // - Clients should not show a channel list for guilds of this type. 21 | // - Clients should show this guild in the guild list with the profile picture 22 | // and the username of the other user. 23 | // - Servers should "upgrade" this guild to a "room" guild if another 24 | // user joins the guild. A name should be crafted using the algorithm 25 | // described below: 26 | // - Get at most 3 members' usernames, by their 27 | // - Concat them with ", " as a seperator. 28 | message DirectMessage { 29 | // Whether this direct message was rejected by the invitee or not. 30 | bool rejected = 1; 31 | } 32 | 33 | // The kind. If this is empty, assume it is `Normal`. 34 | oneof kind { 35 | // A "normal" guild. 36 | Normal normal = 1; 37 | // A "room" guild. 38 | Room room = 2; 39 | // A "direct message" guild. 40 | DirectMessage direct_message = 3; 41 | } 42 | } 43 | 44 | // Object representing a guild without the ID part. 45 | message Guild { 46 | // The name of the guild. 47 | // 48 | // This will be empty if the guild kind is "direct message". See 49 | // the documentation of "direct message" guild kind on how to display 50 | // a name for those guilds. 51 | string name = 1; 52 | // The picture HMC of the guild. 53 | optional string picture = 2; 54 | // User ID of the owners of the guild. 55 | repeated uint64 owner_ids = 3; 56 | // The kind of this guild. 57 | GuildKind kind = 4; 58 | // Metadata of the guild. 59 | optional harmonytypes.v1.Metadata metadata = 5; 60 | } 61 | 62 | // Object representing a guild with the ID part. 63 | message GuildWithId { 64 | // The ID of the guild. 65 | uint64 guild_id = 1; 66 | // The guild. 67 | Guild guild = 2; 68 | } 69 | 70 | // Object representing an invite without the ID part. 71 | message Invite { 72 | // Possible uses of this invite. A use of `0` means infinite uses. 73 | uint32 possible_uses = 1; 74 | // Total use count of this invite. 75 | uint32 use_count = 2; 76 | } 77 | 78 | // Invite with ID. 79 | message InviteWithId { 80 | // ID of the invite. 81 | string invite_id = 1; 82 | // The invite data. 83 | Invite invite = 2; 84 | } 85 | 86 | // A pending invite. 87 | message PendingInvite { 88 | // Invite ID of the invite. 89 | string invite_id = 1; 90 | // Server ID of the server the inviter is on. 91 | optional string server_id = 2; 92 | // User ID of the inviter. 93 | uint64 inviter_id = 3; 94 | } 95 | 96 | // Object representing a guild list entry. 97 | message GuildListEntry { 98 | // Guild ID of this guild entry. 99 | uint64 guild_id = 1; 100 | // Server ID of the homeserver of this guild. 101 | string server_id = 2; 102 | } 103 | 104 | // A reason for why a user has left a guild. 105 | enum LeaveReason { 106 | // The user left the guild willingly. 107 | LEAVE_REASON_WILLINGLY_UNSPECIFIED = 0; 108 | // The user was banned from the guild. 109 | LEAVE_REASON_BANNED = 1; 110 | // The user was kicked from the guild. 111 | LEAVE_REASON_KICKED = 2; 112 | } 113 | 114 | // Request type used in `CreateGuild` endpoint. 115 | message CreateGuildRequest { 116 | // The name of the guild. 117 | string name = 1; 118 | // The picture HMC of the guild. 119 | optional string picture = 2; 120 | // Metadata of the guild. 121 | optional harmonytypes.v1.Metadata metadata = 3; 122 | } 123 | // Used in the `CreateGuild` endpoint. 124 | message CreateGuildResponse { 125 | // Guild ID of the guild that was created. 126 | uint64 guild_id = 1; 127 | } 128 | 129 | // Request type used in `CreateRoom` endpoint. 130 | message CreateRoomRequest { 131 | // The name of the guild. 132 | string name = 1; 133 | // The picture HMC of the guild. 134 | optional string picture = 2; 135 | // Metadata of the guild. 136 | optional harmonytypes.v1.Metadata metadata = 3; 137 | } 138 | // Used in the `CreateRoom` endpoint. 139 | message CreateRoomResponse { 140 | // Guild ID of the guild that was created. 141 | uint64 guild_id = 1; 142 | } 143 | 144 | // Used in the `CreateDirectMessage` endpoint. 145 | message CreateDirectMessageRequest { 146 | // The user name of the user to DM with. 147 | string user_name = 1; 148 | // The server ID of the server the user is on. 149 | // 150 | // Should be left unspecified if it's a user on the homeserver. 151 | optional string server_id = 2; 152 | } 153 | // Used in the `CreateDirectMessage` endpoint. 154 | message CreateDirectMessageResponse { 155 | // Guild ID of the just created "direct message" guild. 156 | uint64 guild_id = 1; 157 | } 158 | 159 | // Used in the `CreateInvite` endpoint. 160 | message CreateInviteRequest { 161 | // Guild ID of the guild to create an invite in. 162 | uint64 guild_id = 1; 163 | // The name of the invite. 164 | string name = 2; 165 | // The possible uses of the invite. 166 | // 167 | // A possible use of `0` means that the invite can be used infinitely many times. 168 | uint32 possible_uses = 3; 169 | } 170 | // Used in the `CreateInvite` endpoint. 171 | message CreateInviteResponse { 172 | // The invite ID of the invite that was created. 173 | string invite_id = 1; 174 | } 175 | 176 | // Used in the `GetGuildList` endpoint. 177 | message GetGuildListRequest {} 178 | // Used in the `GetGuildList` endpoint. 179 | message GetGuildListResponse { 180 | // Guild list returned by the server. 181 | repeated GuildListEntry guilds = 1; 182 | } 183 | 184 | // Used in the `GetGuild` endpoint. 185 | message GetGuildRequest { 186 | // Guild ID of the guild to get information about. 187 | uint64 guild_id = 1; 188 | } 189 | // Used in the `GetGuild` endpoint. 190 | message GetGuildResponse { 191 | // The information of the guild requested. 192 | Guild guild = 1; 193 | } 194 | 195 | // Used in the `GetGuildInvites` endpoint. 196 | message GetGuildInvitesRequest { 197 | // Guild ID of the guild you want to get invites of. 198 | uint64 guild_id = 1; 199 | } 200 | // Used in the `GetGuildInvites` endpoint. 201 | message GetGuildInvitesResponse { 202 | // The invites of the guild, with IDs. 203 | repeated InviteWithId invites = 1; 204 | } 205 | 206 | // Used in the `GetGuildMembers` endpoint. 207 | message GetGuildMembersRequest { 208 | // Guild ID of the guild you want to get members of. 209 | uint64 guild_id = 1; 210 | } 211 | // Used in the `GetGuildMembers` endpoint. 212 | message GetGuildMembersResponse { 213 | // User IDs of all the guild members. 214 | repeated uint64 members = 1; 215 | } 216 | 217 | // Used in the `UpdateGuildInformation` endpoint. 218 | message UpdateGuildInformationRequest { 219 | // Guild ID of the guild you want to update the information of. 220 | uint64 guild_id = 1; 221 | // New name for the guild. 222 | optional string new_name = 2; 223 | // New picture for the guild. 224 | optional string new_picture = 3; 225 | // New metadata for the guild. 226 | optional harmonytypes.v1.Metadata new_metadata = 4; 227 | } 228 | // Used in the `UpdateGuildInformation` endpoint. 229 | message UpdateGuildInformationResponse {} 230 | 231 | // Used in the `UpgradeRoomToGuild` endpoint. 232 | message UpgradeRoomToGuildRequest { 233 | // Guild ID of the "room" guild to upgrade to a "normal" guild. 234 | uint64 guild_id = 1; 235 | } 236 | // Used in the `UpgradeRoomToGuild` endpoint. 237 | message UpgradeRoomToGuildResponse {} 238 | 239 | // Used in the `DeleteGuild` endpoint. 240 | message DeleteGuildRequest { 241 | // Guild ID of the guild you want to delete. 242 | uint64 guild_id = 1; 243 | } 244 | // Used in the `DeleteGuild` endpoint. 245 | message DeleteGuildResponse {} 246 | 247 | // Used in the `DeleteInvite` endpoint. 248 | message DeleteInviteRequest { 249 | // Guild ID of the guild where the invite is located. 250 | uint64 guild_id = 1; 251 | // Invite ID of the invite you want to delete. 252 | string invite_id = 2; 253 | } 254 | // Used in the `DeleteInvite` endpoint. 255 | message DeleteInviteResponse {} 256 | 257 | // Used in the `JoinGuild` endpoint. 258 | message JoinGuildRequest { 259 | // Invite ID of the guild you want to join. 260 | string invite_id = 1; 261 | } 262 | // Used in the `JoinGuild` endpoint. 263 | message JoinGuildResponse { 264 | // Guild ID of the guild you joined. 265 | uint64 guild_id = 1; 266 | } 267 | 268 | // Used in the `PreviewGuild` endpoint. 269 | message PreviewGuildRequest { 270 | // Invite ID of the guild you want to get information from. 271 | string invite_id = 1; 272 | } 273 | // Used in the `PreviewGuild` endpoint. 274 | message PreviewGuildResponse { 275 | // Name of the guild requested. 276 | string name = 1; 277 | // Picture of the guild requested. 278 | optional string picture = 2; 279 | // Member count of the guild requested. 280 | uint64 member_count = 3; 281 | } 282 | 283 | // Used in the `LeaveGuild` endpoint. 284 | message LeaveGuildRequest { 285 | // Guild ID of the guild you want to leave. 286 | uint64 guild_id = 1; 287 | } 288 | // Used in the `LeaveGuild` endpoint. 289 | message LeaveGuildResponse {} 290 | 291 | // Used in `BanUser` endpoint. 292 | message BanUserRequest { 293 | // The guild ID of the guild to ban the user from. 294 | uint64 guild_id = 1; 295 | // The ID of the user to ban. 296 | uint64 user_id = 2; 297 | } 298 | // Used in `BanUser` endpoint. 299 | message BanUserResponse {} 300 | 301 | // Used in `KickUser` endpoint. 302 | message KickUserRequest { 303 | // The guild ID of the guild to kick the user from. 304 | uint64 guild_id = 1; 305 | // The user ID of the user to kick. 306 | uint64 user_id = 2; 307 | } 308 | // Used in `KickUser` endpoint. 309 | message KickUserResponse {} 310 | 311 | // Used in `UnbanUser` endpoint. 312 | message UnbanUserRequest { 313 | // The guild ID of the guild to unban the user from. 314 | uint64 guild_id = 1; 315 | // The user ID of the user to unban. 316 | uint64 user_id = 2; 317 | } 318 | // Used in `UnbanUser` endpoint. 319 | message UnbanUserResponse {} 320 | 321 | // Used in `GetBannedUsers` endpoint. 322 | message GetBannedUsersRequest { 323 | // Guild ID to get banned users for. 324 | uint64 guild_id = 1; 325 | } 326 | // Used in `GetBannedUsers` endpoint. 327 | message GetBannedUsersResponse { 328 | // The user IDs of banned users. 329 | repeated uint64 banned_users = 1; 330 | } 331 | 332 | // Request for GrantOwnership 333 | message GrantOwnershipRequest { 334 | // Guild ID of the guild to give a user ownership on. 335 | uint64 guild_id = 1; 336 | // The ID of the new owner to add. 337 | uint64 new_owner_id = 2; 338 | } 339 | // Response for GrantOwnership 340 | message GrantOwnershipResponse {} 341 | 342 | // Request for GiveUpOwnership 343 | message GiveUpOwnershipRequest { 344 | // Guild ID to give up your ownership on. 345 | uint64 guild_id = 1; 346 | } 347 | // Response for GiveUpOwnership 348 | message GiveUpOwnershipResponse {} 349 | 350 | // Used in `GetPendingInvites` endpoint. 351 | message GetPendingInvitesRequest {} 352 | // Used in `GetPendingInvites` endpoint. 353 | message GetPendingInvitesResponse { 354 | // The pending invite(s). 355 | repeated PendingInvite pending_invites = 1; 356 | } 357 | 358 | // Used in `RejectPendingInvite` endpoint. 359 | message RejectPendingInviteRequest { 360 | // Invite ID of the pending invite to reject. 361 | string invite_id = 1; 362 | // Server ID of the pending invite to reject. 363 | optional string server_id = 2; 364 | } 365 | // Used in `RejectPendingInvite` endpoint. 366 | message RejectPendingInviteResponse {} 367 | 368 | // Used in `IgnorePendingInvite` endpoint. 369 | message IgnorePendingInviteRequest { 370 | // ID of the pending invite to ignore. 371 | string invite_id = 1; 372 | // Server ID of the pending invite to reject. 373 | optional string server_id = 2; 374 | } 375 | // Used in `IgnorePendingInvite` endpoint. 376 | message IgnorePendingInviteResponse {} 377 | 378 | // Used in `InviteUserToGuild` endpoint. 379 | message InviteUserToGuildRequest { 380 | // User name of the user to invite. 381 | string user_name = 1; 382 | // Server ID of the user if they are on another server. 383 | optional string server_id = 2; 384 | // Guild ID of the guild to invite to. 385 | uint64 guild_id = 3; 386 | } 387 | // Used in `InviteUserToGuild` endpoint. 388 | message InviteUserToGuildResponse {} -------------------------------------------------------------------------------- /stable/chat/v1/messages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.chat.v1; 4 | 5 | import "harmonytypes/v1/types.proto"; 6 | import "emote/v1/types.proto"; 7 | 8 | option go_package = "github.com/harmony-development/legato/gen/chat/v1"; 9 | 10 | // Overrides provide a way to override the name and avatar of a message. 11 | message Overrides { 12 | // the overridden username. 13 | optional string username = 1; 14 | // the overridden avatar. 15 | optional string avatar = 2; 16 | // the reason for overriding username and avatar. 17 | oneof reason { 18 | // a custom reason in case the builtin ones don't fit 19 | string user_defined = 3; 20 | // the override occured because of a webhook 21 | harmonytypes.v1.Empty webhook = 4; 22 | // plurality, not system as in computer 23 | harmonytypes.v1.Empty system_plurality = 5; 24 | // the override occured because it was made by the server 25 | // 26 | // Servers should reject messages sent by users with this override. 27 | harmonytypes.v1.Empty system_message = 6; 28 | // the override occured because of bridging 29 | harmonytypes.v1.Empty bridge = 7; 30 | } 31 | } 32 | 33 | // The payload sent to the bot when an action is triggered. 34 | message ActionPayload { 35 | // The payload data for a button action 36 | message Button { 37 | // The data from the Button action 38 | bytes data = 1; 39 | } 40 | // The payload for a dropdown action 41 | message Dropdown { 42 | // The user choice from the dropdown. 43 | bytes choice = 1; 44 | } 45 | // The payload for a text input action 46 | message Input { 47 | // The user input. 48 | string input = 1; 49 | // The bot-provided data 50 | bytes data = 2; 51 | } 52 | // The payload data 53 | oneof payload { 54 | // Payload for a button 55 | Button button = 1; 56 | // Payload for a dropdown 57 | Dropdown dropdown = 2; 58 | // Payload for a text input 59 | Input input = 3; 60 | } 61 | } 62 | 63 | // Actions are interactive elements that can exist within an embed. 64 | message Action { 65 | // The action type. This is primarily used to change the look of the action to 66 | // the user (example: Destructive actions will have a red background). 67 | enum Type { 68 | // a normal action. 69 | TYPE_NORMAL_UNSPECIFIED = 0; 70 | // a primary action. 71 | TYPE_PRIMARY = 1; 72 | // A destructive / dangerous action. 73 | TYPE_DESTRUCTIVE = 2; 74 | } 75 | 76 | // A button that users can click on to trigger an action. 77 | message Button { 78 | // The text to show on the button. 79 | string text = 1; 80 | 81 | // Action data, which should be used in the call to perform the action. 82 | bytes data = 2; 83 | 84 | // An external URL that the button links to. 85 | // This makes it so that tapping this button will open said URL instead 86 | // of triggering the action. 87 | optional string url = 3; 88 | } 89 | 90 | // A dropdown menu that users can click on to trigger an action. 91 | message Dropdown { 92 | // The text describing the dropdown. 93 | string label = 1; 94 | 95 | // An entry in the dropdown 96 | message Entry { 97 | // The dropdown's UI label. 98 | string label = 1; 99 | 100 | // The dropdown's associated data. 101 | bytes data = 2; 102 | } 103 | 104 | // The options in the dropdown. 105 | repeated Entry entries = 2; 106 | } 107 | // A text input that users can type in to trigger an action. 108 | message Input { 109 | // The label describing the input. 110 | string label = 1; 111 | // Whether this text input should be a multiline one or not. 112 | bool multiline = 2; 113 | // Contextual data allowing the bot to discern what the user input is for 114 | bytes data = 3; 115 | } 116 | 117 | // Type of the action. 118 | Type action_type = 1; 119 | 120 | // The kind of the action. 121 | oneof kind { 122 | // Button action. 123 | Button button = 2; 124 | // Dropdown action. 125 | Dropdown dropdown = 3; 126 | // Input action. 127 | Input input = 4; 128 | } 129 | } 130 | 131 | // Object representing a message embed. 132 | message Embed { 133 | // Object representing an embed heading. 134 | message EmbedHeading { 135 | // Text of the heading. 136 | string text = 1; 137 | // Subtext of the heading. 138 | optional string subtext = 2; 139 | // URL of the heading. 140 | optional string url = 3; 141 | // Icon of the heading. 142 | optional string icon = 4; 143 | } 144 | 145 | // Object representing an embed field. 146 | message EmbedField { 147 | // Type representing how to present an embed field. 148 | enum Presentation { 149 | // Show the field as data. 150 | PRESENTATION_DATA_UNSPECIFIED = 0; 151 | // Show the field as a captioned image. 152 | PRESENTATION_CAPTIONED_IMAGE = 1; 153 | // Show the field as a row. 154 | PRESENTATION_ROW = 2; 155 | } 156 | 157 | // Title of this field. 158 | string title = 1; 159 | // Subtitle of this field. 160 | optional string subtitle = 2; 161 | // Body text of this field (eg. a description). 162 | optional FormattedText body = 3; 163 | // Image URL of this field. 164 | optional string image_url = 4; 165 | // How to present this field. 166 | Presentation presentation = 5; 167 | // Actions of this field. 168 | repeated Action actions = 6; 169 | } 170 | 171 | // Title of this embed. 172 | string title = 1; 173 | // Body text of this embed. 174 | optional FormattedText body = 2; 175 | // Color of this embed. 176 | optional int32 color = 3; 177 | // Embed heading for the header. 178 | optional EmbedHeading header = 4; 179 | // Embed heading for the footer. 180 | optional EmbedHeading footer = 5; 181 | // Fields of this embed. 182 | repeated EmbedField fields = 6; 183 | } 184 | 185 | /* 186 | Minithumbnail is an extremely low-quality JPEG thumbnail. 187 | 188 | The resolution is usually no larger than 64x64. 189 | */ 190 | message Minithumbnail { 191 | // The width of the minithumbnail 192 | uint32 width = 1; 193 | // The height of the minithumbnail 194 | uint32 height = 2; 195 | // The JPEG data of the minithumbnail 196 | bytes data = 3; 197 | } 198 | 199 | /* 200 | Photo contains data about a photo. 201 | 202 | Photo are always JPEG, and are 203 | constrained to the following rules: 204 | 205 | - width+height <= 10_000 206 | - width <= height*20 207 | - height <= width*20 208 | 209 | Photos are preferably no more than 10MB 210 | in size, and servers may compress as necessary. 211 | */ 212 | message Photo { 213 | // The HMC URL of the photo. 214 | string hmc = 1; 215 | // The filename of the photo. 216 | string name = 2; 217 | // The size of the photo. 218 | uint32 file_size = 3; 219 | // The height of the photo, in pixels. 220 | uint32 height = 4; 221 | // The width of the photo, in pixels. 222 | uint32 width = 5; 223 | // The photo's caption. 224 | FormattedText caption = 6; 225 | // A thumbnail representing the photo. 226 | Minithumbnail minithumbnail = 7; 227 | } 228 | 229 | // Object representing a generic message attachment. 230 | message Attachment { 231 | // File ID of this attachment. 232 | string id = 1; 233 | // Filename of this attachment. 234 | string name = 2; 235 | // Mimetype of this attachment. 236 | string mimetype = 3; 237 | // Size of this attachment. 238 | uint32 size = 4; 239 | // Caption of this attachment. 240 | optional FormattedText caption = 5; 241 | } 242 | 243 | // Object representing a message's content. 244 | message Content { 245 | // Object representing text content. 246 | message TextContent { 247 | // Text content. 248 | FormattedText content = 1; 249 | } 250 | // Object representing embed content. 251 | message EmbedContent { 252 | // Embed content. 253 | repeated Embed embeds = 1; 254 | } 255 | // Object representing attachment content. 256 | message AttachmentContent { 257 | // A list of attachments. 258 | repeated Attachment files = 1; 259 | } 260 | // Object representing photo content. 261 | message PhotoContent { 262 | // A list of photos. 263 | repeated Photo photos = 1; 264 | } 265 | // Represents a user rejecting an invite. 266 | // 267 | // This can only be used by servers themselves. Servers should reject 268 | // messages with this content if they are sent by a user. 269 | message InviteRejected { 270 | // User ID of the invitee. 271 | uint64 invitee_id = 1; 272 | // User ID of the inviter. 273 | uint64 inviter_id = 2; 274 | } 275 | // Represents a user accepting an invite. 276 | // 277 | // This can only be used by servers themselves. Servers should reject 278 | // messages with this content if they are sent by a user. 279 | message InviteAccepted { 280 | // User ID of the invitee. 281 | uint64 invitee_id = 1; 282 | // User ID of the inviter. 283 | uint64 inviter_id = 2; 284 | } 285 | // Represents a guild upgrade from "room" to "normal". 286 | // 287 | // This can only be used by servers themselves. Servers should reject 288 | // messages with this content if they are sent by a user. 289 | message RoomUpgradedToGuild { 290 | // User ID of the user that upgraded the guild. 291 | uint64 upgraded_by = 1; 292 | } 293 | 294 | // Content data. 295 | oneof content { 296 | // Text content. 297 | TextContent text_message = 1; 298 | // Embed content. 299 | EmbedContent embed_message = 2; 300 | // Attachment content. 301 | AttachmentContent attachment_message = 3; 302 | // Photo content. 303 | PhotoContent photo_message = 4; 304 | // A user rejected an invite. 305 | InviteRejected invite_rejected = 5; 306 | // A user accepted an invite. 307 | InviteAccepted invite_accepted = 6; 308 | // A user upgraded a guild from "room" to "normal". 309 | RoomUpgradedToGuild room_upgraded_to_guild = 7; 310 | } 311 | } 312 | 313 | // Object representing a reaction. 314 | message Reaction { 315 | // Emote data for this reaction. 316 | // 317 | // Emote's image ID is used as an identifier for unique reactions. 318 | // Emotes with the same names should be "deduplicated" by a client, by suffixing 319 | // their names with `~1`, `~2` and so on. 320 | emote.v1.Emote emote = 1; 321 | // How many reactions this reaction has. 322 | uint32 count = 2; 323 | } 324 | 325 | // A format for text 326 | message Format { 327 | // Bold text 328 | message Bold { } 329 | // Italic text 330 | message Italic { } 331 | // Underlined text 332 | message Underline { } 333 | // Monospace text 334 | message Monospace { } 335 | // Superscript text 336 | message Superscript { } 337 | // Subscript text 338 | message Subscript { } 339 | // A larger codeblock, with a programming language specified 340 | // Clients should ideally not bound the width of codeblock messages, 341 | // possibly scrolling the codeblock horizontally separately from the 342 | // rest of the message 343 | message CodeBlock { 344 | // programming language of the code block 345 | string language = 1; 346 | } 347 | // Mention of a user (on the current homeserver) 348 | message UserMention { 349 | // user_id of the user being mentioned 350 | uint64 user_id = 1; 351 | } 352 | // Mention of a role (on the current guild) 353 | message RoleMention { 354 | // the role being mentioned 355 | uint64 role_id = 1; 356 | } 357 | // Mention of a channel (on the current guild) 358 | message ChannelMention { 359 | // the channel being mentioned 360 | uint64 channel_id = 1; 361 | } 362 | // Mention of a guild 363 | message GuildMention { 364 | // the guild being mentioned 365 | uint64 guild_id = 1; 366 | // which homeserver it belongs to 367 | string homeserver = 2; 368 | } 369 | // An emoji 370 | message Emoji { 371 | // The HMC URL of the emoji 372 | string image_hmc = 1; 373 | // The ID of the emoji pack the emoji is from 374 | uint64 pack_id = 2; 375 | } 376 | // Colour modification 377 | message Color { 378 | // The kind of colour modification to apply 379 | enum Kind { 380 | // Dimmed colour 381 | KIND_DIM_UNSPECIFIED = 0; 382 | // Brightened colour 383 | KIND_BRIGHT = 1; 384 | // Negative colour (usually red) 385 | KIND_NEGATIVE = 2; 386 | // Positive colour (usually green) 387 | KIND_POSITIVE = 3; 388 | // Informational colour (usually blue) 389 | KIND_INFO = 4; 390 | // Warning colour (usually yellow-orange) 391 | KIND_WARNING = 5; 392 | } 393 | 394 | // The kind of colour modification to apply 395 | Kind kind = 1; 396 | } 397 | // Replace a part of the text with the text matching the i18n code. 398 | // If i18n code was not found, keep the original text. 399 | message Localization { 400 | // i18n code for the text. 401 | string i18n_code = 1; 402 | } 403 | 404 | // where the format begins to apply to 405 | uint32 start = 1; 406 | // how many characters the format is 407 | uint32 length = 2; 408 | 409 | // the style if format to apply to this text 410 | oneof format { 411 | // a text format for bold text 412 | Bold bold = 3; 413 | // a text format for italic text 414 | Italic italic = 4; 415 | // a text format for underline text 416 | Underline underline = 5; 417 | // a text format for monospace text 418 | Monospace monospace = 6; 419 | // a text format for superscript text 420 | Superscript superscript = 7; 421 | // a text format for subscript text 422 | Subscript subscript = 8; 423 | // a text format for a codeblock 424 | CodeBlock code_block = 9; 425 | // a text format for a user mention 426 | UserMention user_mention = 10; 427 | // a text format for a role mention 428 | RoleMention role_mention = 11; 429 | // a text format for a channel mention 430 | ChannelMention channel_mention = 12; 431 | // a text format for a guild mention 432 | GuildMention guild_mention = 13; 433 | // a text format for an emoji 434 | Emoji emoji = 14; 435 | // a text format for coloured text 436 | Color color = 15; 437 | // a text format for localization 438 | Localization localization = 16; 439 | } 440 | } 441 | 442 | // Formatted text 443 | message FormattedText { 444 | // The textual content of a message 445 | string text = 1; 446 | // The formats for a message 447 | repeated Format format = 2; 448 | } 449 | 450 | // Object representing a message without the ID part. 451 | message Message { 452 | // Metadata of this message. 453 | optional harmonytypes.v1.Metadata metadata = 1; 454 | // Overrides of this message. 455 | Overrides overrides = 2; 456 | // User ID of the user who sent this message. 457 | uint64 author_id = 3; 458 | // When this message was created, in miliseconds since unix epoch 459 | uint64 created_at = 4; 460 | // The most recent time this message was edited, in milliseconds since unix epoch 461 | optional uint64 edited_at = 5; 462 | // The message this message is a reply to. 463 | optional uint64 in_reply_to = 6; 464 | // The content of the message. 465 | Content content = 7; 466 | // The reactions of the message. 467 | repeated Reaction reactions = 8; 468 | } 469 | 470 | // Object representing a message with it's ID. 471 | message MessageWithId { 472 | // ID of the message. 473 | uint64 message_id = 1; 474 | // The message data. 475 | Message message = 2; 476 | } 477 | 478 | // Used in the `GetChannelMessages` endpoint. 479 | message GetChannelMessagesRequest { 480 | // The direction relative to the `message_id` message to get messages from. 481 | enum Direction { 482 | // Get messages before the anchor. 483 | DIRECTION_BEFORE_UNSPECIFIED = 0; 484 | // Get messages around the anchor, including the anchor. 485 | DIRECTION_AROUND = 1; 486 | // Get messages after the anchor. 487 | DIRECTION_AFTER = 2; 488 | } 489 | 490 | // Guild ID of the guild that has the channel. 491 | uint64 guild_id = 1; 492 | // Channel ID of the channel to get messages from. 493 | uint64 channel_id = 2; 494 | // The ID of the message that will be used as an "anchor" point to figure out 495 | // where to get the messages. 496 | // If not specified, the `direction` will be ignored and the newest messages 497 | // will be returned. 498 | optional uint64 message_id = 3; 499 | // On which direction to get the messages. 500 | // 501 | // - By default, it is "before", which means you will get messages before the 502 | // `message_id` message. 503 | // - If it is "around", you will get the messages around the `message_id` 504 | // message. This will include the `message_id` message itself, as the middle 505 | // item of the list returned. 506 | // - If it is "after", you will get the messages after the `message_id` 507 | // message. 508 | optional Direction direction = 4; 509 | // How many messages to get. 510 | // 511 | // - If `0`, a recommended message count to return is 25. If the direction is 512 | // "around", the recommended value is 12. 513 | // - If the direction to get the messages is "around", this count will not be 514 | // the *total* count of messages to return, but instead the count of messages 515 | // to return *for each direction*, before and after. 516 | // - Servers should enforce their own maximum limit, and clamp this value to 517 | // the limit. 518 | optional uint32 count = 5; 519 | } 520 | // Used in the `GetChannelMessages` endpoint. 521 | message GetChannelMessagesResponse { 522 | // Has reached the top (first message) of the message history. 523 | bool reached_top = 1; 524 | // Has reached the bottom (last message) of the message history. 525 | bool reached_bottom = 2; 526 | // The messages requested. 527 | repeated MessageWithId messages = 3; 528 | } 529 | 530 | // Used in the `GetMessage` endpoint. 531 | message GetMessageRequest { 532 | // Guild ID of the guild where the channel is. 533 | uint64 guild_id = 1; 534 | // Channel ID of the channel where the message is. 535 | uint64 channel_id = 2; 536 | // Message ID of the message you want to get. 537 | uint64 message_id = 3; 538 | } 539 | // Used in the `GetMessage` endpoint. 540 | message GetMessageResponse { 541 | // The message requested. 542 | Message message = 1; 543 | } 544 | 545 | // Used in the `DeleteMessage` endpoint. 546 | message DeleteMessageRequest { 547 | // Guild ID of the guild where the channel is. 548 | uint64 guild_id = 1; 549 | // Channel ID of the channel where the message is. 550 | uint64 channel_id = 2; 551 | // Message ID of the message you want to delete. 552 | uint64 message_id = 3; 553 | } 554 | // Used in the `DeleteMessage` endpoint. 555 | message DeleteMessageResponse {} 556 | 557 | // Used in the `TriggerAction` endpoint. 558 | message TriggerActionRequest { 559 | // Guild ID of the guild where the channel is. 560 | uint64 guild_id = 1; 561 | // Channel ID of the channel where the message is. 562 | uint64 channel_id = 2; 563 | // Message ID of the message you want to trigger an action in. 564 | uint64 message_id = 3; 565 | // Payload of action data. 566 | ActionPayload payload = 4; 567 | } 568 | // Used in the `TriggerAction` endpoint. 569 | message TriggerActionResponse {} 570 | 571 | // Used in the `SendMessage` endpoint. 572 | message SendMessageRequest { 573 | // Guild ID of the guild where the channel is. 574 | uint64 guild_id = 1; 575 | // Channel ID of the channel you want to send a message in. 576 | uint64 channel_id = 2; 577 | // Content of the new message. 578 | Content content = 3; 579 | // Echo ID of the new message. This can be used by clients to 580 | // determine whether a message is sent. 581 | optional uint64 echo_id = 4; 582 | // The overrides of this new message. 583 | optional Overrides overrides = 6; 584 | // The message this new message is a reply to. 585 | optional uint64 in_reply_to = 7; 586 | 587 | // The metadata of this new message. 588 | optional harmonytypes.v1.Metadata metadata = 5; 589 | } 590 | // Used in the `SendMessage` endpoint. 591 | message SendMessageResponse { 592 | // Message ID of the message sent. 593 | uint64 message_id = 1; 594 | } 595 | 596 | // Used in the `UpdateMessageText` endpoint. 597 | message UpdateMessageTextRequest { 598 | // Guild ID of the guild where the channel is. 599 | uint64 guild_id = 1; 600 | // Channel ID of the channel where the message is. 601 | uint64 channel_id = 2; 602 | // Message ID of the message you want to edit the text of. 603 | uint64 message_id = 3; 604 | // New content for this message. 605 | FormattedText new_content = 4; 606 | } 607 | // Used in the `UpdateMessageText` endpoint. 608 | message UpdateMessageTextResponse {} 609 | 610 | // Used in the `PinMessage` endpoint. 611 | message PinMessageRequest { 612 | // Guild ID of the guild where the channel is. 613 | uint64 guild_id = 1; 614 | // Channel ID of the channel where the message is. 615 | uint64 channel_id = 2; 616 | // Message ID of the message we want to pin. 617 | uint64 message_id = 3; 618 | } 619 | // Used in the `UnpinMessage` endpoint. 620 | message PinMessageResponse {} 621 | 622 | // Used in the `UnpinMessage` endpoint. 623 | message UnpinMessageRequest { 624 | // Guild ID of the guild where the channel is. 625 | uint64 guild_id = 1; 626 | // Channel ID of the channel where the message is. 627 | uint64 channel_id = 2; 628 | // Message ID of the message we want to unpin. 629 | uint64 message_id = 3; 630 | } 631 | // Used in the `UnpinMessage` endpoint. 632 | message UnpinMessageResponse {} 633 | 634 | // Used in the `GetPinnedMessages` endpoint. 635 | message GetPinnedMessagesRequest { 636 | // Guild ID of the guild where the channel is. 637 | uint64 guild_id = 1; 638 | // Channel ID of the channel we want to get pins of. 639 | uint64 channel_id = 2; 640 | } 641 | 642 | // Used in the `GetPinnedMessages` endpoint. 643 | message GetPinnedMessagesResponse { 644 | // The IDs of the pinned messages. 645 | repeated uint64 pinned_message_ids = 1; 646 | } 647 | 648 | // Used in `AddReaction` endpoint. 649 | message AddReactionRequest { 650 | // Guild ID of the guild where the channel is. 651 | uint64 guild_id = 1; 652 | // Channel ID of the channel where the message is. 653 | uint64 channel_id = 2; 654 | // Message ID of the message we want to add a reaction to. 655 | uint64 message_id = 3; 656 | // The emote we want to react with. 657 | emote.v1.Emote emote = 4; 658 | } 659 | // Used in `AddReaction` endpoint. 660 | message AddReactionResponse {} 661 | 662 | // Used in `RemoveReaction` endpoint. 663 | message RemoveReactionRequest { 664 | // Guild ID of the guild where the channel is. 665 | uint64 guild_id = 1; 666 | // Channel ID of the channel where the message is. 667 | uint64 channel_id = 2; 668 | // Message ID of the message we want to remove a reaction. 669 | uint64 message_id = 3; 670 | // The emote we want to remove the react of. 671 | emote.v1.Emote emote = 4; 672 | } 673 | // Used in `RemoveReaction` endpoint. 674 | message RemoveReactionResponse {} 675 | -------------------------------------------------------------------------------- /stable/chat/v1/permissions.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.chat.v1; 4 | 5 | import "harmonytypes/v1/types.proto"; 6 | 7 | option go_package = "github.com/harmony-development/legato/gen/chat/v1"; 8 | 9 | // Object representing a single permission node. 10 | message Permission { 11 | // the permission matcher. (example: roles.manage) 12 | string matches = 1; 13 | // whether the permission is allowed or not. 14 | bool ok = 2; 15 | } 16 | 17 | // Object representing a role without the ID. 18 | message Role { 19 | // the role name. 20 | string name = 1; 21 | // the role color. 22 | int32 color = 2; 23 | // whether the role is hoisted or not. 24 | bool hoist = 3; 25 | // whether the role is mentionable or not. 26 | bool pingable = 4; 27 | } 28 | 29 | // Object representing a role with it's ID. 30 | // 31 | // The role ID for the default role in a guild should always be 0. 32 | message RoleWithId { 33 | // ID of the role. 34 | uint64 role_id = 1; 35 | // The role data. 36 | Role role = 2; 37 | } 38 | 39 | // Used in the `QueryHasPermission` endpoint. 40 | message QueryHasPermissionRequest { 41 | // the guild ID to query permissions for 42 | uint64 guild_id = 1; 43 | // the channel ID to query permissions for. If not set, it will query 44 | // permissions for the guild. 45 | optional uint64 channel_id = 2; 46 | // the user ID to query permissions for (if not provided, the current user is 47 | // assumed). 48 | optional uint64 as = 4; 49 | // the permission node to check for. 50 | string check_for = 3; 51 | } 52 | // Used in the `QueryHasPermission` endpoint. 53 | message QueryHasPermissionResponse { 54 | // the permissions for the given node. 55 | bool ok = 1; 56 | } 57 | 58 | // Used in the `SetPermissions` endpoint. 59 | message SetPermissionsRequest { 60 | // the guild ID to set permissions for. 61 | uint64 guild_id = 1; 62 | // the channel ID to set permissions for. Only set if the role is for a 63 | // channel. 64 | optional uint64 channel_id = 2; 65 | // the role ID to set permissions for. 66 | uint64 role_id = 3; 67 | // the permission list to give. 68 | // 69 | // There is no "perms_to_take" because not given permissions are by 70 | // default not allowed. 71 | repeated Permission perms_to_give = 4; 72 | } 73 | // Used in the `SetPermissions` endpoint. 74 | message SetPermissionsResponse {} 75 | 76 | // Used in the `GetPermissions` endpoint. 77 | message GetPermissionsRequest { 78 | // the guild ID to get permissions for. 79 | uint64 guild_id = 1; 80 | // the channel ID to get permissions for. Only applicable for roles in a 81 | // channel. 82 | optional uint64 channel_id = 2; 83 | // the role ID to get permissions for. 84 | uint64 role_id = 3; 85 | } 86 | // Used in the `GetPermissions` endpoint. 87 | message GetPermissionsResponse { 88 | // the permissions list for the given role. 89 | repeated Permission perms = 1; 90 | } 91 | 92 | // Used in the `MoveRole` endpoint. 93 | message MoveRoleRequest { 94 | // the guild ID to move the role in. 95 | uint64 guild_id = 1; 96 | // the role ID to move. 97 | uint64 role_id = 2; 98 | // the new position of the role. 99 | harmonytypes.v1.ItemPosition new_position = 3; 100 | } 101 | // Used in the `MoveRole` endpoint. 102 | message MoveRoleResponse {} 103 | 104 | // Used in the `GetGuildRoles` endpoint. 105 | message GetGuildRolesRequest { 106 | // the guild ID to get roles for. 107 | uint64 guild_id = 1; 108 | } 109 | // Used in the `GetGuildRoles` endpoint. 110 | message GetGuildRolesResponse { 111 | // the list of roles in the guild. 112 | repeated RoleWithId roles = 1; 113 | } 114 | 115 | // Used in the `AddGuildRole` endpoint. 116 | message AddGuildRoleRequest { 117 | // the guild ID to add the role to. 118 | uint64 guild_id = 1; 119 | // the role name. 120 | string name = 2; 121 | // the role color. 122 | int32 color = 3; 123 | // whether the role is hoisted or not. 124 | bool hoist = 4; 125 | // whether the role is mentionable or not. 126 | bool pingable = 5; 127 | } 128 | // Used in the `AddGuildRole` endpoint. 129 | message AddGuildRoleResponse { 130 | // the ID of the newly created role. 131 | uint64 role_id = 1; 132 | } 133 | 134 | // Used in the `DeleteGuildRole` endpoint. 135 | message DeleteGuildRoleRequest { 136 | // the guild ID to delete the role from. 137 | uint64 guild_id = 1; 138 | // the role ID to delete. 139 | uint64 role_id = 2; 140 | } 141 | // Used in the `DeleteGuildRole` endpoint. 142 | message DeleteGuildRoleResponse {} 143 | 144 | // Used in the `ModifyGuildRole` endpoint. 145 | message ModifyGuildRoleRequest { 146 | // the ID of the guild where the role is located 147 | uint64 guild_id = 1; 148 | // the ID of the role to modify 149 | uint64 role_id = 2; 150 | // the new name of the role 151 | optional string new_name = 3; 152 | // the new color of the role 153 | optional int32 new_color = 4; 154 | // the new hoist status of the role 155 | optional bool new_hoist = 5; 156 | // the new pingable status of the role 157 | optional bool new_pingable = 6; 158 | } 159 | // Used in the `ModifyGuildRole` endpoint. 160 | message ModifyGuildRoleResponse {} 161 | 162 | // Used in the `ManageUserRoles` endpoint. 163 | message ManageUserRolesRequest { 164 | // the ID of the guild where the user is being managed 165 | uint64 guild_id = 1; 166 | // the ID of the user to modify 167 | uint64 user_id = 2; 168 | // the IDs of the roles to add 169 | repeated uint64 give_role_ids = 3; 170 | // the IDs of the roles to remove 171 | repeated uint64 take_role_ids = 4; 172 | } 173 | // Used in the `ManageUserRoles` endpoint. 174 | message ManageUserRolesResponse {} 175 | 176 | // Used in the `GetUserRoles` endpoint. 177 | message GetUserRolesRequest { 178 | // the ID of the guild where the user is located 179 | uint64 guild_id = 1; 180 | // the ID of the user to get roles for 181 | uint64 user_id = 2; 182 | } 183 | // Used in the `GetUserRoles` endpoint. 184 | message GetUserRolesResponse { 185 | // a list of IDs of the roles the user has 186 | repeated uint64 roles = 1; 187 | } 188 | -------------------------------------------------------------------------------- /stable/chat/v1/stream.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.chat.v1; 4 | 5 | import "harmonytypes/v1/types.proto"; 6 | import "chat/v1/channels.proto"; 7 | import "chat/v1/guilds.proto"; 8 | import "chat/v1/messages.proto"; 9 | import "chat/v1/permissions.proto"; 10 | import "emote/v1/stream.proto"; 11 | import "profile/v1/stream.proto"; 12 | 13 | option go_package = "github.com/harmony-development/legato/gen/chat/v1"; 14 | 15 | // Request type for use in the `StreamEvents` endpoint. 16 | // By default, this endpoint will subscribe to all events. 17 | // Any guilds joined in the future will be added to the subscription as well. 18 | // Use the UnsubscribeFromAll event for unsubscribing from all current subscriptions and disable the automatic guild subscriptions 19 | message StreamEventsRequest { 20 | // Event source for guilds' events. 21 | message SubscribeToGuild { 22 | // the guild id to subscribe to 23 | uint64 guild_id = 1; 24 | } 25 | // Event source for actions' events. 26 | message SubscribeToActions {} 27 | // Event source for homeserver events. 28 | message SubscribeToHomeserverEvents {} 29 | // Event to unsubscribe from all events. 30 | message UnsubscribeFromAll {} 31 | 32 | // Describes which event source to subscribe to. 33 | oneof request { 34 | // Subscribe to the guild event source. 35 | SubscribeToGuild subscribe_to_guild = 1; 36 | // Subscribe to the action event source. 37 | SubscribeToActions subscribe_to_actions = 2; 38 | // Subscribe to the homeserver event source. 39 | SubscribeToHomeserverEvents subscribe_to_homeserver_events = 3; 40 | // Unsubscribe from all events. 41 | UnsubscribeFromAll unsubscribe_from_all = 4; 42 | } 43 | } 44 | 45 | // Used in the `StreamEvents` endpoint. 46 | message StreamEventsResponse { 47 | // Possible events. 48 | oneof event { 49 | // A chat event. 50 | StreamEvent chat = 1; 51 | // A emote event. 52 | emote.v1.StreamEvent emote = 2; 53 | // A profile event. 54 | profile.v1.StreamEvent profile = 3; 55 | } 56 | } 57 | 58 | // Describes an event. 59 | message StreamEvent { 60 | // Event sent when a new message is sent. 61 | message MessageSent { 62 | // ID that is sent by your client it can use to confirm that the message is sent. 63 | optional uint64 echo_id = 1; 64 | // Guild ID of the guild where this event happened. 65 | uint64 guild_id = 2; 66 | // Channel ID of the channel where this event happened. 67 | uint64 channel_id = 3; 68 | // Message ID of the message that was updated. 69 | uint64 message_id = 4; 70 | // The actual message. 71 | Message message = 5; 72 | } 73 | 74 | // Event sent when a message's text content is updated. 75 | message MessageUpdated { 76 | // Guild ID of the guild where this event happened. 77 | uint64 guild_id = 1; 78 | // Channel ID of the channel where this event happened. 79 | uint64 channel_id = 2; 80 | // Message ID of the message that was updated. 81 | uint64 message_id = 3; 82 | // When this message was edited, in milliseconds since unix epoch 83 | uint64 edited_at = 4; 84 | // New message content. 85 | chat.v1.FormattedText new_content = 5; 86 | } 87 | 88 | // Event sent when a message is deleted. 89 | message MessageDeleted { 90 | // Guild ID of the guild where this event happened. 91 | uint64 guild_id = 1; 92 | // Channel ID of the channel where this event happened. 93 | uint64 channel_id = 2; 94 | // Message ID of the message that was deleted. 95 | uint64 message_id = 3; 96 | } 97 | 98 | // Event sent when a new channel is created. 99 | message ChannelCreated { 100 | // Guild ID of the guild where this event happened. 101 | uint64 guild_id = 1; 102 | // Channel ID of the channel where this event happened. 103 | uint64 channel_id = 2; 104 | // Name of this channel. 105 | string name = 3; 106 | // The position in the channel list. 107 | harmonytypes.v1.ItemPosition position = 4; 108 | // The kind of this channel. 109 | chat.v1.ChannelKind kind = 5; 110 | // Metadata for this channel. 111 | optional harmonytypes.v1.Metadata metadata = 6; 112 | } 113 | 114 | // Event sent when a channel's information is changed. 115 | message ChannelUpdated { 116 | // Guild ID of the guild where this event happened. 117 | uint64 guild_id = 1; 118 | // Channel ID of the channel that was changed. 119 | uint64 channel_id = 2; 120 | // The new name of the channel. 121 | optional string new_name = 3; 122 | // The new metadata of the channel. 123 | optional harmonytypes.v1.Metadata new_metadata = 4; 124 | } 125 | 126 | // Event sent when a channel's position in the channel list is changed. 127 | message ChannelPositionUpdated { 128 | // Guild ID of the guild where this event happened. 129 | uint64 guild_id = 1; 130 | // Channel ID of the channel that was changed. 131 | uint64 channel_id = 2; 132 | // The new position of the channel. 133 | optional harmonytypes.v1.ItemPosition new_position = 3; 134 | } 135 | 136 | // Event sent when all channels have been reordered 137 | message ChannelsReordered { 138 | // guild_id: the guild whose channels are being reordered 139 | uint64 guild_id = 2; 140 | // channel_ids: the new order of channel IDs 141 | repeated uint64 channel_ids = 1; 142 | } 143 | 144 | // Event sent when a channel is deleted. 145 | message ChannelDeleted { 146 | // Guild ID of the guild where this event happened. 147 | uint64 guild_id = 1; 148 | // Channel ID of the channel that was deleted. 149 | uint64 channel_id = 2; 150 | } 151 | 152 | // Event sent when a guild's information is changed. 153 | message GuildUpdated { 154 | // Guild ID of the guild that was changed. 155 | uint64 guild_id = 1; 156 | // The new name of the guild. 157 | optional string new_name = 2; 158 | // The new picture of the guild. 159 | optional string new_picture = 3; 160 | // The new metadata of the guild. 161 | optional harmonytypes.v1.Metadata new_metadata = 4; 162 | } 163 | 164 | // Event sent when a guild is deleted. 165 | message GuildDeleted { 166 | // Guild ID of the guild that was deleted. 167 | uint64 guild_id = 1; 168 | } 169 | 170 | // Event sent a user joins to a guild. 171 | message MemberJoined { 172 | // Member ID of the member that joined the guild. 173 | uint64 member_id = 1; 174 | // Guild ID of the guild where this event happened. 175 | uint64 guild_id = 2; 176 | } 177 | 178 | // Event sent when a member of a guild leaves said guild for whatever reason. 179 | message MemberLeft { 180 | // User ID of the member that left the guild. 181 | uint64 member_id = 1; 182 | // Guild ID of the guild where this event happened. 183 | uint64 guild_id = 2; 184 | // Why this member left the guild. 185 | chat.v1.LeaveReason leave_reason = 3; 186 | } 187 | 188 | // Event sent when you join a new guild. 189 | message GuildAddedToList { 190 | // Guild ID of the guild where this event happened. 191 | uint64 guild_id = 1; 192 | // The homeserver this guild is on. 193 | string homeserver = 2; 194 | } 195 | 196 | // Event sent when you leave a guild. 197 | message GuildRemovedFromList { 198 | // Guild ID of the guild where this event happened. 199 | uint64 guild_id = 1; 200 | // The homeserver this guild is on. 201 | string homeserver = 2; 202 | } 203 | 204 | // Event sent when an action is performed. 205 | message ActionPerformed { 206 | // Guild ID of the guild where this event happened. 207 | uint64 guild_id = 1; 208 | // Channel ID of the channel where this event happened. 209 | uint64 channel_id = 2; 210 | // Message ID where this event happened. 211 | uint64 message_id = 3; 212 | // User ID of the user that triggered the action 213 | uint64 user_id = 4; 214 | // The action data payload 215 | ActionPayload payload = 5; 216 | } 217 | 218 | // Event sent when a role's position in the role list is changed. 219 | message RoleMoved { 220 | // Guild ID of the guild where this event happened. 221 | uint64 guild_id = 1; 222 | // Role ID of the role that was moved. 223 | uint64 role_id = 2; 224 | // New position of the role. 225 | harmonytypes.v1.ItemPosition new_position = 3; 226 | } 227 | 228 | // Event sent when a role is deleted. 229 | message RoleDeleted { 230 | // Guild ID of the guild where this event happened. 231 | uint64 guild_id = 1; 232 | // Role ID of the role that was deleted. 233 | uint64 role_id = 2; 234 | } 235 | 236 | // Event sent when a role is created. 237 | message RoleCreated { 238 | // Guild ID of the guild where this event happened. 239 | uint64 guild_id = 1; 240 | // Role ID of the role that was created. 241 | uint64 role_id = 2; 242 | // The name of the role. 243 | string name = 3; 244 | // The color of the role. 245 | int32 color = 4; 246 | // The hoist status of the role. 247 | bool hoist = 5; 248 | // The pingable status of the role. 249 | bool pingable = 6; 250 | } 251 | 252 | // Event sent when a role's information is changed. 253 | message RoleUpdated { 254 | // Guild ID of the guild where this event happened. 255 | uint64 guild_id = 1; 256 | // Role ID of the role that was changed. 257 | uint64 role_id = 2; 258 | // The new name of the role. 259 | optional string new_name = 3; 260 | // The new color of the role. 261 | optional int32 new_color = 4; 262 | // The new hoist status of the role. 263 | optional bool new_hoist = 5; 264 | // The new pingable status of the role. 265 | optional bool new_pingable = 6; 266 | } 267 | 268 | // Event sent when a role's permissions are changed. 269 | // 270 | // This event will only be sent to users with the "guild.manage" permission. 271 | message RolePermissionsUpdated { 272 | // Guild ID of the guild where this event happened. 273 | uint64 guild_id = 1; 274 | // Channel ID of the channel where this event happened. 275 | optional uint64 channel_id = 2; 276 | // Role ID of the role that had it's permissions changed. 277 | uint64 role_id = 3; 278 | // The new permissions. 279 | repeated chat.v1.Permission new_perms = 4; 280 | } 281 | 282 | // Event sent when a user's roles are changed. 283 | message UserRolesUpdated { 284 | // Guild ID of the guild where this event happened. 285 | uint64 guild_id = 1; 286 | // User ID of the user that had it's roles changed. 287 | uint64 user_id = 2; 288 | // The new role IDs. 289 | repeated uint64 new_role_ids = 3; 290 | } 291 | 292 | // Event sent when a user sends a typing notification in a guild channel. 293 | message Typing { 294 | // User ID of the user that sent the typing notification. 295 | uint64 user_id = 1; 296 | // Guild ID of the guild where this event happened. 297 | uint64 guild_id = 2; 298 | // Channel ID of the channel where this event happened. 299 | uint64 channel_id = 3; 300 | } 301 | 302 | // Event sent when a permission is changed that matters to you. 303 | // 304 | // Servers should calculate which users to send this event to when a permission is set. 305 | // It should only be sent if a user is subscribed to the guild the permission pertains to. 306 | message PermissionUpdated { 307 | // Guild ID of the guild where this event happened. 308 | uint64 guild_id = 1; 309 | // Channel ID of the channel where this event happened. 310 | optional uint64 channel_id = 2; 311 | // The permission node that was changed. 312 | string query = 3; 313 | // Whether you have the permission or not. 314 | bool ok = 4; 315 | } 316 | 317 | // Sent when a message is pinned in a guild channel. 318 | // 319 | // Should only be sent to users who have the "message.view" permission for 320 | // the guild channel where the message was pinned. 321 | message MessagePinned { 322 | // Guild ID of the guild where this event occured. 323 | uint64 guild_id = 1; 324 | // Channel ID of the channel where this event occured. 325 | uint64 channel_id = 2; 326 | // Message ID of the message that was pinned. 327 | uint64 message_id = 3; 328 | } 329 | 330 | // Sent when a message is unpinned in a guild channel. 331 | // 332 | // Should only be sent to users who have the "message.view" permission for 333 | // the guild channel where the message was unpinned. 334 | message MessageUnpinned { 335 | // Guild ID of the guild where this event occured. 336 | uint64 guild_id = 1; 337 | // Channel ID of the channel where this event occured. 338 | uint64 channel_id = 2; 339 | // Message ID of the message that was unpinned. 340 | uint64 message_id = 3; 341 | } 342 | 343 | // Sent when a message's reaction is changed. 344 | message ReactionUpdated { 345 | // Guild ID of the guild where this event occured. 346 | uint64 guild_id = 1; 347 | // Channel ID of the channel where this event occured. 348 | uint64 channel_id = 2; 349 | // Message ID of the message that had a reaction updated. 350 | uint64 message_id = 3; 351 | // The reaction. 352 | chat.v1.Reaction reaction = 4; 353 | } 354 | 355 | // Sent when there's a new owner. 356 | message OwnerAdded { 357 | // User ID of the new owner. 358 | uint64 user_id = 1; 359 | } 360 | 361 | // Sent when an owner gives up their ownership. 362 | message OwnerRemoved { 363 | // User ID of the user who is no longer owner. 364 | uint64 user_id = 1; 365 | } 366 | 367 | // Sent when a guild invite is received. 368 | message InviteReceived { 369 | // ID of the invite received. 370 | string invite_id = 1; 371 | // Server ID of the server the inviter is on. 372 | optional string server_id = 2; 373 | // User ID of the inviter. 374 | uint64 inviter_id = 3; 375 | } 376 | 377 | // Sent when a guild invite is rejected by the invitee. 378 | message InviteRejected { 379 | // Guild ID of the guild that this occured for. 380 | uint64 guild_id = 1; 381 | // ID of the invite rejected. 382 | string invite_id = 2; 383 | // User ID of the invitee. 384 | uint64 user_id = 3; 385 | } 386 | 387 | // Which event to send. 388 | oneof event { 389 | // Send the guild added to list event. 390 | GuildAddedToList guild_added_to_list = 1; 391 | // Send the guild removed from list event. 392 | GuildRemovedFromList guild_removed_from_list = 2; 393 | // Send the action performed event. 394 | ActionPerformed action_performed = 3; 395 | // Send the message sent event. 396 | MessageSent sent_message = 4; 397 | // Send the message updated event. 398 | MessageUpdated edited_message = 5; 399 | // Send the message deleted event. 400 | MessageDeleted deleted_message = 6; 401 | // Send the channel created event. 402 | ChannelCreated created_channel = 7; 403 | // Send the channel updated event. 404 | ChannelUpdated edited_channel = 8; 405 | // Send the channel deleted event. 406 | ChannelDeleted deleted_channel = 9; 407 | // Send the guild updated event. 408 | GuildUpdated edited_guild = 10; 409 | // Send the guild deleted event. 410 | GuildDeleted deleted_guild = 11; 411 | // Send the member joined event. 412 | MemberJoined joined_member = 12; 413 | // Send the member left event. 414 | MemberLeft left_member = 13; 415 | // Send the typing event. 416 | Typing typing = 14; 417 | // Send the role created event. 418 | RoleCreated role_created = 15; 419 | // Send the role deleted event. 420 | RoleDeleted role_deleted = 16; 421 | // Send the role moved event. 422 | RoleMoved role_moved = 17; 423 | // Send the role updated event. 424 | RoleUpdated role_updated = 18; 425 | // Send the role perms updated event. 426 | RolePermissionsUpdated role_perms_updated = 19; 427 | // Send the user roles updated event. 428 | UserRolesUpdated user_roles_updated = 20; 429 | // Send the permission updated event. 430 | PermissionUpdated permission_updated = 21; 431 | // The channels have been completely reordered. 432 | ChannelsReordered channels_reordered = 22; 433 | // Send the channel position updated event. 434 | ChannelPositionUpdated edited_channel_position = 23; 435 | // Send the message pinned event. 436 | MessagePinned message_pinned = 24; 437 | // Send the message unpinned event. 438 | MessageUnpinned message_unpinned = 25; 439 | // Send the reaction updated event. 440 | ReactionUpdated reaction_updated = 26; 441 | // Send the owner added event. 442 | OwnerAdded owner_added = 27; 443 | // Send the owner removed event. 444 | OwnerRemoved owner_removed = 28; 445 | // Send the guild invite received event. 446 | InviteReceived invite_received = 29; 447 | // Send the guild invite rejected event. 448 | InviteRejected invite_rejected = 30; 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /stable/emote/v1/emote.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "harmonytypes/v1/types.proto"; 4 | import "emote/v1/types.proto"; 5 | 6 | package protocol.emote.v1; 7 | 8 | // Harmony's Emote service manages the emotes and emote packs. 9 | service EmoteService { 10 | // Endpoint to create an emote pack. 11 | rpc CreateEmotePack(CreateEmotePackRequest) 12 | returns (CreateEmotePackResponse) { 13 | option (harmonytypes.v1.metadata).requires_authentication = true; 14 | } 15 | // Endpoint to get the emote packs you have equipped. 16 | rpc GetEmotePacks(GetEmotePacksRequest) returns (GetEmotePacksResponse) { 17 | option (harmonytypes.v1.metadata).requires_authentication = true; 18 | } 19 | // Endpoint to get the emotes in an emote pack. 20 | rpc GetEmotePackEmotes(GetEmotePackEmotesRequest) returns (GetEmotePackEmotesResponse) { 21 | option (harmonytypes.v1.metadata).requires_authentication = true; 22 | } 23 | // Endpoint to add an emote to an emote pack that you own. 24 | rpc AddEmoteToPack(AddEmoteToPackRequest) returns (AddEmoteToPackResponse) { 25 | option (harmonytypes.v1.metadata).requires_authentication = true; 26 | } 27 | // Endpoint to delete an emote pack that you own. 28 | rpc DeleteEmotePack(DeleteEmotePackRequest) returns (DeleteEmotePackResponse) { 29 | option (harmonytypes.v1.metadata).requires_authentication = true; 30 | } 31 | 32 | // Endpoint to delete an emote from an emote pack. 33 | rpc DeleteEmoteFromPack(DeleteEmoteFromPackRequest) returns (DeleteEmoteFromPackResponse) { 34 | option (harmonytypes.v1.metadata).requires_authentication = true; 35 | } 36 | 37 | // Endpoint to dequip an emote pack that you have equipped. 38 | rpc DequipEmotePack(DequipEmotePackRequest) returns (DequipEmotePackResponse) { 39 | option (harmonytypes.v1.metadata).requires_authentication = true; 40 | } 41 | 42 | // Endpoint to equip an emote pack. 43 | rpc EquipEmotePack(EquipEmotePackRequest) returns (EquipEmotePackResponse) { 44 | option (harmonytypes.v1.metadata).requires_authentication = true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /stable/emote/v1/stream.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "emote/v1/types.proto"; 4 | 5 | package protocol.emote.v1; 6 | 7 | // Event sent when an emote pack's information is changed. 8 | // 9 | // Should only be sent to users who have the pack equipped. 10 | message EmotePackUpdated { 11 | // ID of the pack that was updated. 12 | uint64 pack_id = 1; 13 | // New pack name of the pack. 14 | optional string new_pack_name = 2; 15 | } 16 | 17 | // Event sent when an emote pack is deleted. 18 | // 19 | // Should only be sent to users who have the pack equipped. 20 | // Should also be sent if a user dequips an emote pack, only to the user that dequipped it. 21 | message EmotePackDeleted { 22 | // ID of the pack that was deleted. 23 | uint64 pack_id = 1; 24 | } 25 | 26 | // Event sent when an emote pack is added. 27 | // 28 | // Should only be sent to the user who equipped the pack. 29 | message EmotePackAdded { 30 | // Emote pack that was equipped by the user. 31 | EmotePack pack = 1; 32 | } 33 | 34 | // Event sent when an emote pack's emotes were changed. 35 | // 36 | // Should only be sent to users who have the pack equipped. 37 | message EmotePackEmotesUpdated { 38 | // ID of the pack to update the emotes of. 39 | uint64 pack_id = 1; 40 | // The added emotes. 41 | repeated Emote added_emotes = 2; 42 | // The names of the deleted emotes. 43 | repeated string deleted_emotes = 3; 44 | } 45 | 46 | // Describes an emote service event. 47 | message StreamEvent { 48 | // The event type. 49 | oneof event { 50 | // Send the emote pack added event. 51 | EmotePackAdded emote_pack_added = 1; 52 | // Send the emote pack updated event. 53 | EmotePackUpdated emote_pack_updated = 2; 54 | // Send the emote pack deleted event. 55 | EmotePackDeleted emote_pack_deleted = 3; 56 | // Send the emote pack emotes updated event. 57 | EmotePackEmotesUpdated emote_pack_emotes_updated = 4; 58 | } 59 | } -------------------------------------------------------------------------------- /stable/emote/v1/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.emote.v1; 4 | 5 | // Data for a single pack of emotes. 6 | message EmotePack { 7 | // The ID of the pack. 8 | uint64 pack_id = 1; 9 | // The ID of the user who created the pack. 10 | uint64 pack_owner = 2; 11 | // The name of the pack. 12 | string pack_name = 3; 13 | } 14 | 15 | // Data for a single emote. 16 | message Emote { 17 | // The image ID of the emote. This is the ID of the image in the image store 18 | // (same place attachments are stored). 19 | string image_id = 1; 20 | // The name of the emote. 21 | string name = 2; 22 | } 23 | 24 | // Used in the `CreateEmotePack` endpoint. 25 | message CreateEmotePackRequest { 26 | // the name of the pack. 27 | string pack_name = 1; 28 | } 29 | // Used in the `CreateEmotePack` endpoint. 30 | message CreateEmotePackResponse { 31 | // The ID of the pack. 32 | uint64 pack_id = 1; 33 | } 34 | 35 | // Used in the `GetEmotePacks` endpoint. 36 | message GetEmotePacksRequest {} 37 | // Used in the `GetEmotePacks` endpoint. 38 | message GetEmotePacksResponse { 39 | // The list of emote packs. 40 | repeated EmotePack packs = 1; 41 | } 42 | 43 | // Used in the `GetEmotes` endpoint. 44 | message GetEmotePackEmotesRequest { 45 | // The ID of the pack. 46 | uint64 pack_id = 1; 47 | } 48 | // Used in the `GetEmotes` endpoint. 49 | message GetEmotePackEmotesResponse { 50 | // The list of emotes in the pack. 51 | repeated Emote emotes = 1; 52 | } 53 | 54 | // Used in the `AddEmoteToPack` endpoint. 55 | message AddEmoteToPackRequest { 56 | // The ID of the pack. 57 | uint64 pack_id = 1; 58 | // The emote to add. 59 | Emote emote = 2; 60 | } 61 | // Used in the `AddEmoteToPack` endpoint. 62 | message AddEmoteToPackResponse {} 63 | 64 | // Used in the `DeleteEmoteFromPack` endpoint. 65 | message DeleteEmoteFromPackRequest { 66 | // The ID of the pack. 67 | uint64 pack_id = 1; 68 | // The name of the emote to delete. 69 | string name = 2; 70 | } 71 | // Used in the `DeleteEmoteFromPack` endpoint. 72 | message DeleteEmoteFromPackResponse {} 73 | 74 | // Used in the `DeleteEmotePack` endpoint. 75 | message DeleteEmotePackRequest { 76 | // The ID of the pack. 77 | uint64 pack_id = 1; 78 | } 79 | // Used in the `DeleteEmotePack` endpoint. 80 | message DeleteEmotePackResponse {} 81 | 82 | // Used in the `DequipEmotePack` endpoint. 83 | message DequipEmotePackRequest { 84 | // The ID of the pack. 85 | uint64 pack_id = 1; 86 | } 87 | // Used in the `DequipEmotePack` endpoint. 88 | message DequipEmotePackResponse {} 89 | 90 | // Used in the `EquipEmotePack` endpoint. 91 | message EquipEmotePackRequest { 92 | // The ID of the pack. 93 | uint64 pack_id = 1; 94 | } 95 | // Used in the `EquipEmotePack` endpoint. 96 | message EquipEmotePackResponse {} -------------------------------------------------------------------------------- /stable/harmonytypes/v1/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.harmonytypes.v1; 4 | 5 | option go_package = "github.com/harmony-development/legato/gen/harmonytypes/v1"; 6 | 7 | import "google/protobuf/descriptor.proto"; 8 | 9 | // Metadata for methods. These are set in individual RPC endpoints and are 10 | // typically used by servers. 11 | message HarmonyMethodMetadata { 12 | // whether the method requires authentication. 13 | bool requires_authentication = 1; 14 | // whether the method allows federation or not. 15 | bool requires_local = 2; 16 | // the permission nodes required to invoke the method. 17 | string requires_permission_node = 3; 18 | // whether the method requires owner 19 | bool requires_owner = 4; 20 | } 21 | 22 | extend google.protobuf.MethodOptions { 23 | // Harmony method metadata. 24 | HarmonyMethodMetadata metadata = 1091; 25 | } 26 | 27 | // Anything holds anything 28 | message Anything { 29 | // Kind is the kind of the message 30 | string kind = 1; 31 | // Body is the serialised bytes 32 | bytes body = 2; 33 | } 34 | 35 | // Metadata type used by many messages. 36 | message Metadata { 37 | // Kind of this metadata. 38 | string kind = 1; 39 | // A map containing information. 40 | map extension = 2; 41 | } 42 | 43 | // Error type that will be returned by servers. 44 | message Error { 45 | // The identifier of this error, can be used as an i18n key. 46 | string identifier = 1; 47 | // A (usually english) human message for this error. 48 | string human_message = 2; 49 | // More details about this message. Is dependent on the endpoint. 50 | bytes more_details = 3; 51 | } 52 | 53 | // Token that will be used for authentication. 54 | message Token { 55 | // Ed25519 signature of the following serialized protobuf data, signed 56 | // with a private key. Which private key used to sign will be described 57 | // in the documentation. 58 | // 59 | // Has to be 64 bytes long, otherwise it will be rejected. 60 | bytes sig = 1; 61 | // Serialized protobuf data. 62 | // The protobuf type of this serialized data is dependent on the API endpoint 63 | // used. 64 | bytes data = 2; 65 | } 66 | 67 | // An empty message 68 | message Empty { } 69 | 70 | // An object representing an item position between two other items. 71 | message ItemPosition { 72 | // The position 73 | enum Position { 74 | // The position is before the item 75 | POSITION_BEFORE_UNSPECIFIED = 0; 76 | // The position is after the item 77 | POSITION_AFTER = 1; 78 | } 79 | 80 | // The ID of the item the position is relative to 81 | uint64 item_id = 1; 82 | // Whether the position is before or after the given ID 83 | Position position = 2; 84 | } 85 | -------------------------------------------------------------------------------- /stable/mediaproxy/v1/mediaproxy.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.mediaproxy.v1; 4 | import "harmonytypes/v1/types.proto"; 5 | 6 | option go_package = "github.com/harmony-development/legato/gen/mediaproxy/v1"; 7 | 8 | // Object representing the metadata of a website. 9 | message SiteMetadata { 10 | // Title of the website. 11 | string site_title = 1; 12 | // Page title of the website page. 13 | string page_title = 2; 14 | // Kind of the website. 15 | string kind = 3; 16 | // Description of the website page. 17 | string description = 4; 18 | // URL of the website. 19 | string url = 5; 20 | // A thumbnail image of the website. 21 | string image = 6; 22 | } 23 | // Object represeting the metadata of a media. 24 | message MediaMetadata { 25 | // Mimetype of the media. 26 | string mimetype = 1; 27 | // Filename of the media. 28 | string filename = 2; 29 | // Sıze of the media. 30 | // 31 | // This should (usually) be the size taken from the `Content-Length` header 32 | // (for HTTP requests). 33 | // If this is not included, then it means the size could not be determined. 34 | optional uint32 size = 3; 35 | } 36 | 37 | // Used in the `FetchLinkMetadata` endpoint. 38 | message FetchLinkMetadataRequest { 39 | // URL to fetch metadata from. 40 | string url = 1; 41 | } 42 | // Used in the `FetchLinkMetadata` endpoint. 43 | message FetchLinkMetadataResponse { 44 | // Data of the metadata. 45 | oneof data { 46 | // Site metadata for the URL. 47 | SiteMetadata is_site = 1; 48 | // Media metadata for the URL. 49 | MediaMetadata is_media = 2; 50 | } 51 | } 52 | 53 | // Used in the `InstantView` endpoint. 54 | message InstantViewRequest { 55 | // URL to get instant view for. 56 | string url = 1; 57 | } 58 | // Used in the `InstantView` endpoint. 59 | message InstantViewResponse { 60 | // Site metadata for the URL. 61 | SiteMetadata metadata = 1; 62 | // Instant view content. 63 | string content = 2; 64 | // Whether the instant view is valid. 65 | bool is_valid = 3; 66 | } 67 | // Used in the `CanInstantView` endpoint. 68 | message CanInstantViewRequest { 69 | // URL to query if server can instant view the website. 70 | string url = 1; 71 | } 72 | // Used in the `CanInstantView` endpoint. 73 | message CanInstantViewResponse { 74 | // Whether the server generate an instant view for the URL queried. 75 | bool can_instant_view = 1; 76 | } 77 | 78 | // Harmony service for fetching metadata and generating instant view for URLs. 79 | service MediaProxyService { 80 | // Endpoint to fetch metadata for a URL. 81 | rpc FetchLinkMetadata(FetchLinkMetadataRequest) returns (FetchLinkMetadataResponse) { 82 | option (harmonytypes.v1.metadata).requires_authentication = true; 83 | } 84 | // Endpoint to instant view a website URL. 85 | rpc InstantView(InstantViewRequest) returns (InstantViewResponse) { 86 | option (harmonytypes.v1.metadata).requires_authentication = true; 87 | } 88 | // Endpoint to query if the server can generate an instant view for a website URL. 89 | rpc CanInstantView(CanInstantViewRequest) returns (CanInstantViewResponse) { 90 | option (harmonytypes.v1.metadata).requires_authentication = true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /stable/name-resolution/example/.gitignore: -------------------------------------------------------------------------------- 1 | /harmony-name-resolution-example 2 | -------------------------------------------------------------------------------- /stable/name-resolution/example/go.mod: -------------------------------------------------------------------------------- 1 | module harmony-name-resolution-example 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /stable/name-resolution/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net" 9 | "net/http" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | func splitHostPort(in string) (string, string) { 15 | host, port, err := net.SplitHostPort(in) 16 | if err != nil && !strings.Contains(err.Error(), "missing port in address") { 17 | log.Fatalf("%+v\n", err) 18 | } else if err != nil { 19 | host = in 20 | } 21 | 22 | return host, port 23 | } 24 | 25 | func main() { 26 | toResolve := os.Args[1] 27 | 28 | host, port := splitHostPort(toResolve) 29 | ip := net.ParseIP(host) 30 | 31 | // if this is an IP... 32 | if ip != nil { 33 | fmt.Printf("resolved address is: %s:%s\n", host, port) 34 | os.Exit(0) 35 | } 36 | 37 | // this is a domain, then 38 | if port == "" { 39 | // let's use the GET method 40 | resp, err := http.Get(fmt.Sprintf("https://%s/_harmony/server", host)) 41 | if resp == nil { 42 | log.Fatalf("error GET-ing https://%s/_harmony/server %+v\n", host, err) 43 | } 44 | 45 | if resp.StatusCode == 404 { 46 | goto assume 47 | } 48 | 49 | var returned struct { 50 | Server string `json:"h.server"` 51 | } 52 | 53 | data, err := ioutil.ReadAll(resp.Body) 54 | if err != nil { 55 | log.Fatalf("error reading body: %+v\n", err) 56 | } 57 | 58 | err = json.Unmarshal(data, &returned) 59 | if err != nil { 60 | log.Fatalf("error reading JSON: %+v\n", err) 61 | } 62 | 63 | host, port = splitHostPort(toResolve) 64 | if port == "" { 65 | port = "2289" 66 | } 67 | ip = net.ParseIP(host) 68 | 69 | // if this is an IP... 70 | if ip != nil { 71 | fmt.Printf("resolved address is: %s:%s\n", host, port) 72 | os.Exit(0) 73 | } 74 | 75 | goto assume 76 | } 77 | 78 | assume: 79 | ips, err := net.LookupIP(host) 80 | if err != nil { 81 | log.Fatalf("failed to resolve IP: %+v\n", err) 82 | } 83 | 84 | if len(ips) < 1 { 85 | log.Fatalf("no IP addresses found\n") 86 | } 87 | 88 | ip = ips[0] 89 | 90 | fmt.Printf("resolved address is: %s:%s\n", ip, port) 91 | os.Exit(0) 92 | } 93 | -------------------------------------------------------------------------------- /stable/name-resolution/name-resolution.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Server Name Resolution" 3 | --- 4 | 5 | Every Harmony homeserver is identified by a "server name." A server name uniquely identifies a server, regardless of whether the resolved IP address changes or not. Two server names pointing to the same IP address would be considered two different homeservers. 6 | 7 | A server name is resolved to an IP address and port using the following process: 8 | 9 | ## When IP Address (`xxx.xxx.xxx.xxx` + optional `:port`) 10 | 11 | When the server name is an IP address, this is the resolved IP of the server that requests should be sent to. If not specified, `:port` is 2289. 12 | 13 | The `Host` header in requests should be set to the IP literal including the port if specified in the server name. 14 | 15 | ## When domain and port (`example.com:port`) 16 | 17 | When the server name is a hostname with a port, the hostname should be resolved to an IP address using AAAA or A record lookups in the DNS. This IP address will be the one requests are sent to. 18 | 19 | The `Host` header in requests should be set to the domain name and given port, not the resolved IP address. 20 | 21 | ## When domain without port (`example.com`) 22 | 23 | When the server name is a host without a port, a request should be made to `https://hostname/_harmony/server`. If the request fails with a 404, resolution of the server name should be done as if it had port 2289 specified using the above method for resolving domains with ports. Otherwise, the request is expected to return a JSON object with the following schema: 24 | 25 | ```json 26 | { 27 | "h.server": "", 28 | } 29 | ``` 30 | 31 | `h.server` should be either an IP address or a domain + port. It may not be another domain without a port. `h.server` should be resolved using the above two methods as per usual to an IP address. This IP address will be the one requests are sent to. 32 | 33 | The `Host` header in requests should be set to the domain name without port. 34 | -------------------------------------------------------------------------------- /stable/profile/v1/appdata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.profile.v1; 4 | 5 | import "harmonytypes/v1/types.proto"; 6 | 7 | // A tag for an override. This is used as a 8 | // standard shorthand for sending a message with 9 | // an override. If a message starts with before and 10 | // ends with after, clients should send a message 11 | // with the override the tag belongs to, stripping 12 | // the tag indicators. 13 | message OverrideTag { 14 | // The portion of the tag before the messge. 15 | string before = 1; 16 | // The portion of the tag after the messge. 17 | string after = 2; 18 | } 19 | 20 | // An individual override 21 | message ProfileOverride { 22 | // The username for this override 23 | optional string username = 1; 24 | // The avatar for this override 25 | optional string avatar = 2; 26 | // The tags for this override. 27 | repeated OverrideTag tags = 3; 28 | // The reason this override is used 29 | oneof reason { 30 | // a custom reason in case the builtin ones don't fit 31 | string user_defined = 4; 32 | // plurality, not system as in computer 33 | harmonytypes.v1.Empty system_plurality = 5; 34 | } 35 | } 36 | 37 | // The message used for the 'h.overrides' key 38 | // of appdata. 39 | message AppDataOverrides { 40 | // The list of overrides. 41 | repeated ProfileOverride overrides = 1; 42 | } -------------------------------------------------------------------------------- /stable/profile/v1/profile.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "harmonytypes/v1/types.proto"; 4 | import "profile/v1/types.proto"; 5 | 6 | package protocol.profile.v1; 7 | 8 | // Harmony's Profile service manages the profiles of the users. 9 | service ProfileService { 10 | // Gets a user's profile. 11 | rpc GetProfile(GetProfileRequest) returns (GetProfileResponse) { 12 | option (harmonytypes.v1.metadata).requires_authentication = true; 13 | } 14 | 15 | // Updates the user's profile. 16 | rpc UpdateProfile(UpdateProfileRequest) returns (UpdateProfileResponse) { 17 | option (harmonytypes.v1.metadata).requires_authentication = true; 18 | } 19 | 20 | // Gets app data for a user (this can be used to store user preferences which 21 | // is synchronized across devices). 22 | rpc GetAppData(GetAppDataRequest) returns (GetAppDataResponse) { 23 | option (harmonytypes.v1.metadata).requires_authentication = true; 24 | } 25 | 26 | // Sets the app data for a user. 27 | rpc SetAppData(SetAppDataRequest) returns (SetAppDataResponse) { 28 | option (harmonytypes.v1.metadata).requires_authentication = true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /stable/profile/v1/stream.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "profile/v1/types.proto"; 4 | 5 | package protocol.profile.v1; 6 | 7 | // Event sent when a user's profile is updated. 8 | // 9 | // Servers should sent this event only to users that can "see" (eg. they are 10 | // in the same guild) the user this event was triggered by. 11 | message ProfileUpdated { 12 | // User ID of the user that had it's profile updated. 13 | uint64 user_id = 1; 14 | // New username for this user. 15 | optional string new_username = 2; 16 | // New avatar for this user. 17 | optional string new_avatar = 3; 18 | // New status for this user. 19 | optional UserStatus new_status = 4; 20 | // New is bot or not for this user. 21 | // Deprecated, prefer new_account_kind if set. 22 | optional bool new_is_bot = 5 [ deprecated = true ]; 23 | // The new account kind for this account 24 | optional AccountKind new_account_kind = 6; 25 | } 26 | 27 | // Describes an emote service event. 28 | message StreamEvent { 29 | // The event type. 30 | oneof event { 31 | // Send the profile updated event. 32 | ProfileUpdated profile_updated = 14; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /stable/profile/v1/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.profile.v1; 4 | 5 | // The possible statuses a user can have. 6 | enum UserStatus { 7 | // User is offline (not connected to the server). 8 | USER_STATUS_OFFLINE_UNSPECIFIED = 0; 9 | // User is online (this is the default value if ommitted). 10 | USER_STATUS_ONLINE = 1; 11 | // User is away. 12 | USER_STATUS_IDLE = 2; 13 | // User does not want to be disturbed. 14 | USER_STATUS_DO_NOT_DISTURB = 3; 15 | // User is on mobile. 16 | USER_STATUS_MOBILE = 4; 17 | // User is streaming 18 | USER_STATUS_STREAMING = 5; 19 | } 20 | 21 | // The possible kinds of an account 22 | enum AccountKind { 23 | // The account is a full-fledged account controlled by a human 24 | ACCOUNT_KIND_FULL_UNSPECIFIED = 0; 25 | // The account is an account controlled by a bot 26 | ACCOUNT_KIND_BOT = 1; 27 | // The account is a guest account controlled by a human 28 | ACCOUNT_KIND_GUEST = 2; 29 | } 30 | 31 | // Data for a single profile, without the user's ID. 32 | message Profile { 33 | // the name of the user. 34 | string user_name = 1; 35 | // the user's avatar. 36 | optional string user_avatar = 2; 37 | // the status of the user. 38 | UserStatus user_status = 3; 39 | // whether the user is a bot or not 40 | bool is_bot = 4 [ deprecated = true ]; 41 | // what kind of account the user is, e.g. full, guest, bot 42 | AccountKind account_kind = 5; 43 | } 44 | 45 | // Used in `GetProfile` endpoint. 46 | message GetProfileRequest { 47 | // The id of the user to get. 48 | uint64 user_id = 1; 49 | } 50 | 51 | // Used in `GetProfile` endpoint. 52 | message GetProfileResponse { 53 | // The user's profile 54 | Profile profile = 1; 55 | } 56 | 57 | // Used in `UpdateProfile` endpoint. 58 | message UpdateProfileRequest { 59 | // new name of the user. 60 | optional string new_user_name = 1; 61 | // new user avatar. The avatar will be removed if the string is empty. 62 | optional string new_user_avatar = 2; 63 | // new status of the user. 64 | optional UserStatus new_user_status = 3; 65 | // new whether the user is a bot or not 66 | // 67 | // Deprecated; see bot service and guest handling 68 | // in auth. 69 | optional bool new_is_bot = 4 [ deprecated = true ]; 70 | } 71 | 72 | // Used in `UpdateProfile` endpoint. 73 | message UpdateProfileResponse {} 74 | 75 | // Used in `GetAppData` endpoint. 76 | message GetAppDataRequest { 77 | // the app id. 78 | string app_id = 1; 79 | } 80 | 81 | // Used in `GetAppData` endpoint. 82 | message GetAppDataResponse { 83 | // the app data. 84 | bytes app_data = 1; 85 | } 86 | 87 | // Used in `SetAppData` endpoint. 88 | message SetAppDataRequest { 89 | // the app id. 90 | string app_id = 1; 91 | // the app data. 92 | bytes app_data = 2; 93 | } 94 | // Used in `SetAppData` endpoint. 95 | message SetAppDataResponse {} 96 | -------------------------------------------------------------------------------- /stable/rest/rest.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: RESTful Endpoints 3 | --- 4 | 5 | ## Authentication 6 | 7 | If an endpoint expects authentication, the user should set the `Authorization` 8 | header to a valid token from the hRPC API. 9 | 10 | ## POST `/_harmony/media/upload` 11 | 12 | Expects authentication: yes 13 | 14 | The body should be POST-ed as a multipart form (`multipart/form-data`), with a 15 | single part named `file` which contains the body of the file being uploaded, 16 | the name of the file and the MIME type of the file. 17 | 18 | ### Responses 19 | 20 | #### 200 OK 21 | 22 | The body will contain a JSON object in the following format: 23 | 24 | | Field | Description | 25 | | ----- | ------------------------------------------ | 26 | | `id` | The file ID of the file that was uploaded. | 27 | 28 | #### 400 Bad Request 29 | 30 | The body will contain a JSON object in the following format: 31 | 32 | | Field | Description | 33 | | --------- | ------------------ | 34 | | `message` | The error message. | 35 | 36 | Possible error messages and their meanings: 37 | 38 | | Error | Description | 39 | | ---------------- | ------------------------------------------------- | 40 | | `missing-files` | You forgot to upload a file. | 41 | | `too-many-files` | You have uploaded more than one `file` form part. | 42 | 43 | ## GET `/_harmony/media/download/:file_id` 44 | 45 | Expects authentication: no 46 | 47 | The `file_id` should be one of the following: 48 | 49 | - An attachment ID returned from POST `/_harmony/media/upload` 50 | - A URI-encoded URL of an image 51 | - A URI-encoded HMC URL. 52 | 53 | ### Responses 54 | 55 | #### 200 OK 56 | 57 | | Header | Value | 58 | | --------------------- | ------------------------------------------------------------------- | 59 | | `Content-Type` | The type of the file being downloaded. | 60 | | `Content-Disposition` | `attachment; filename=` or `inline; filename=`. | 61 | 62 | ##### Body 63 | 64 | The body will contain the content of the requested file. 65 | 66 | ## GET `/_harmony/about` 67 | 68 | Expects authentication: no 69 | 70 | ### Responses 71 | 72 | #### 200 OK 73 | 74 | ##### Body 75 | 76 | The body will contain a JSON object in the following format: 77 | 78 | | Field | Description | 79 | | ----------------- | ----------------------------------------------------------------- | 80 | | `serverName` | the name of the Harmony server software being hosted | 81 | | `version` | the version of said Harmony server software | 82 | | `aboutServer` | A description of why / who this server is hosted for. | 83 | | `messageOfTheDay` | "message of the day", can be used to put maintenance information. | 84 | 85 | Example response: 86 | 87 | ```json 88 | { 89 | "serverName": "Scherzo", 90 | "version": "git-0c062f6", 91 | "aboutServer": "The main Harmony server.", 92 | "messageOfTheDay": "A maintenance will be done between 18:00 - 20:00." 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /stable/sync/v1/sync.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.sync.v1; 4 | 5 | option go_package = "github.com/harmony-development/legato/gen/sync/v1"; 6 | 7 | // Authentication data that will be sent in a `harmonytypes.v1.Token`. 8 | message AuthData { 9 | // The server ID of the server initiating the transaction. For Pull, 10 | // this tells the server being connected to which homeservers' events it should send. 11 | // For Push, this tells the server being connected to which homeservers' events it is 12 | // receiving. 13 | string server_id = 1; 14 | // The UTC UNIX time in seconds of when the request is started. Servers should reject 15 | // tokens with a time too far from the current time, at their discretion. A recommended 16 | // variance is 1 minute. 17 | uint64 time = 2; 18 | } 19 | 20 | // Object representing a postbox event. 21 | message Event { 22 | // Event sent when a user is removed from a guild. 23 | message UserRemovedFromGuild { 24 | // User ID of the user that was removed. 25 | uint64 user_id = 1; 26 | // Guild ID of the guild where the user was. 27 | uint64 guild_id = 2; 28 | } 29 | // Event sent when a user is added to a guild. 30 | message UserAddedToGuild { 31 | // User ID of the user that was added. 32 | uint64 user_id = 1; 33 | // Guild ID of the guild where the user will be. 34 | uint64 guild_id = 2; 35 | } 36 | // Event sent when a user is invited to a guild. 37 | message UserInvited { 38 | // User ID of the invitee. 39 | uint64 user_id = 1; 40 | // User ID of the user that invited. 41 | uint64 inviter_id = 2; 42 | // The unique identifier of a user's invite to another 43 | // user to join a given guild. 44 | string invite_id = 3; 45 | } 46 | // Event sent when a user rejects a guild invitation. 47 | message UserRejectedInvite { 48 | // Guild ID of the guild the invitee rejected an invite for. 49 | uint64 guild_id = 1; 50 | // User ID of the invitee that rejected the invitation. 51 | uint64 user_id = 2; 52 | // Invite ID of the invite that was rejected. 53 | string invite_id = 3; 54 | } 55 | 56 | // The kind and data of this event. 57 | oneof kind { 58 | // User removed from a guild. 59 | UserRemovedFromGuild user_removed_from_guild = 1; 60 | // User added to a guild. 61 | UserAddedToGuild user_added_to_guild = 2; 62 | // User invited to a guild. 63 | UserInvited user_invited = 3; 64 | // User rejected a guild invitation. 65 | UserRejectedInvite user_rejected_invite = 4; 66 | } 67 | } 68 | 69 | // Used in `Pull` endpoint. 70 | message PullRequest {} 71 | // Used in `Pull` endpoint. 72 | message PullResponse { 73 | // The events that were not processed yet. 74 | repeated Event event_queue = 1; 75 | } 76 | 77 | // Used in `Push` endpoint. 78 | message PushRequest { 79 | // The event to push to the server. 80 | Event event = 1; 81 | } 82 | // Used in `Push` endpoint. 83 | message PushResponse {} 84 | 85 | // Used in `NotifyNewId` endpoint. 86 | message NotifyNewIdRequest { 87 | // The new server ID of the server. 88 | string new_server_id = 1; 89 | } 90 | // Used in `NotifyNewId` endpoint. 91 | message NotifyNewIdResponse {} 92 | 93 | // # Postbox 94 | // 95 | // The postbox service forms the core of Harmony's server <-> server communications. 96 | // 97 | // It concerns the transfer of Events between servers, as well as ensuring reliable 98 | // delivery of them. 99 | // 100 | // The semantics of events are documented in the event types. The postbox service 101 | // is solely reliable for reliable pushing and pulling. 102 | // 103 | // ## Server Identification 104 | // 105 | // Servers are identified using their domain, and the port which they serve. This is 106 | // called the "server ID", and must be formatted as `domain:port`. The port is NOT 107 | // optional. Converting this ID to a URL for communicating can simply be done via 108 | // prefixing the ID with a protocol, eg. `https://`. 109 | // 110 | // ## Authorisation 111 | // 112 | // Requests are authorised using a serialized `harmonytypes.v1.Token` in the Authorization HTTP header. 113 | // The `data` field of the token will be a serialized `AuthData` message. 114 | // The private key used to sign is the homeserver's private key. 115 | // 116 | // ## Events 117 | // 118 | // In this section, we will use sender and recipient to refer to the servers 119 | // sending the events and the server receiving the events respectively. 120 | // 121 | // At PostboxService startup, a sender should first Pull all receivers it had 122 | // federated from before. If a receiver makes a Push to the sender while a Pull 123 | // is going on, the Push should be processed after the Pull is completed. 124 | // 125 | // The sender will attempt to Push to the receiver. If the Push RPC fails more 126 | // than X times (a recommended retry count is 5), the event will be dispatched 127 | // to the sender's queue for the receiver. Unless the receiver pulls these events, 128 | // all new events should be dispatched to the queue. No new Push RPC should be made 129 | // before the queue is emptied. This ensures that events are always received in the 130 | // right order. 131 | // 132 | // It is recommended that receivers try pulling periodically, for example, every 133 | // 1 minute after the last Push RPC by the sender. This ensures that events are recieved. 134 | service PostboxService { 135 | // Endpoint to pull events. 136 | rpc Pull(PullRequest) returns (PullResponse) {} 137 | // Endpoint to push events. 138 | rpc Push(PushRequest) returns (PushResponse) {} 139 | // Endpoint to notify a server of a server ID change. It is called by the server 140 | // that had it's server ID changed for all servers it has federated with. 141 | rpc NotifyNewId(NotifyNewIdRequest) returns (NotifyNewIdResponse) {} 142 | } 143 | -------------------------------------------------------------------------------- /staging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | foreach(dir IN ITEMS voice bots) 2 | install(DIRECTORY ${dir} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/harmony-protocols FILES_MATCHING PATTERN "*.proto") 3 | endforeach() 4 | -------------------------------------------------------------------------------- /staging/bots/v1/bots.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protocol.bots.v1; 4 | /** 5 | 6 | ## Bot Invite Codes 7 | 8 | Bot invite codes work similarly to HMC URLs. 9 | They can either be server+code or just code, 10 | to refer to a bot on the current homeserver. 11 | 12 | The format for an invite code is: 13 | > [url[:port]/]code 14 | 15 | where `code` is a purely alphanumeric code. 16 | 17 | */ 18 | 19 | // A description of a bot account 20 | message Bot { 21 | // The ID of the bot 22 | uint64 bot_id = 1; 23 | // The bot's display name 24 | string display_name = 2; 25 | // The bot's avatar URL 26 | string avatar_url = 3; 27 | // The bot's invite code, if it has one 28 | optional string invite = 4; 29 | } 30 | 31 | // Request type for MyBots 32 | message MyBotsRequest {} 33 | // Response type for MyBots 34 | message MyBotsResponse { 35 | // The list of owned bots 36 | repeated Bot bots = 1; 37 | } 38 | 39 | // Request type for CreateBot 40 | message CreateBotRequest { 41 | // The bot's display name 42 | string display_name = 1; 43 | // The bot's avatar URL 44 | optional string avatar_url = 2; 45 | // The bot's invite code, if it has one 46 | optional string invite = 3; 47 | } 48 | // Response type for CreateBot 49 | message CreateBotResponse { 50 | // The newly minted ID of the bot 51 | uint64 bot_id = 1; 52 | } 53 | 54 | // Request type for EditBot 55 | message EditBotRequest { 56 | // The ID of the bot to edit 57 | uint64 bot_id = 1; 58 | // The bot's new display name 59 | optional string new_display_name = 2; 60 | // The bot's new avatar URL 61 | optional string new_avatar_url = 3; 62 | // The bot's new invite code 63 | optional string invite = 4; 64 | } 65 | // Response type for EditBot 66 | message EditBotResponse {} 67 | 68 | // Request type for DeleteBot 69 | message DeleteBotRequest { 70 | // The bot to delete 71 | uint64 bot_id = 1; 72 | } 73 | // Response type for DeleteBot 74 | message DeleteBotResponse {} 75 | 76 | // Request type for GetBot 77 | message GetBotRequest { 78 | // The bot to get the information of 79 | uint64 bot_id = 1; 80 | } 81 | // Response type for GetBot 82 | message GetBotResponse { 83 | // The requested bot 84 | Bot bot = 1; 85 | } 86 | 87 | // Request type for Policies 88 | message PoliciesRequest {} 89 | // Response type for Policies 90 | message PoliciesResponse { 91 | // How many bots an individual account is allowed to own 92 | uint32 max_bots = 1; 93 | } 94 | 95 | // Request type for AddBot 96 | message AddBotRequest { 97 | // The guild to add the bot to 98 | uint64 guild_id = 1; 99 | // The bot's invite code. 100 | string invite_code = 2; 101 | } 102 | // Response type for AddBot 103 | message AddBotResponse {} 104 | 105 | // The Bots service allows the management of bot accounts 106 | service BotsService { 107 | // Gets the list of bots that you own 108 | rpc MyBots(MyBotsRequest) returns (MyBotsResponse); 109 | // Gets information on a given bot 110 | rpc GetBot(GetBotRequest) returns (GetBotResponse); 111 | // Creates a new bot account 112 | rpc CreateBot(CreateBotRequest) returns (CreateBotResponse); 113 | // Modifies a bot account that you own 114 | rpc EditBot(EditBotRequest) returns (EditBotResponse); 115 | // Deletes a bot account that you own 116 | rpc DeleteBot(DeleteBotRequest) returns (DeleteBotResponse); 117 | // Server policies for bot accounts that the client 118 | // may display in its UI or restrict actions to prevent 119 | // request errors 120 | rpc Policies(PoliciesRequest) returns (PoliciesResponse); 121 | // Requests a bot to add itself to the guild. 122 | // 123 | // For cross-server bots, this dispatches a UserInvited 124 | // request across sync, inviting the bot to the guild. 125 | rpc AddBot(AddBotRequest) returns (AddBotResponse); 126 | }; 127 | -------------------------------------------------------------------------------- /staging/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | name: harmonyapp.io/harmony-development/protocol 3 | lint: 4 | use: 5 | - DEFAULT 6 | - COMMENTS 7 | except: 8 | - PACKAGE_DIRECTORY_MATCH 9 | breaking: 10 | use: 11 | - FILE 12 | -------------------------------------------------------------------------------- /staging/voice/v1/voice.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "harmonytypes/v1/types.proto"; 4 | 5 | package protocol.voice.v1; 6 | 7 | option go_package = "github.com/harmony-development/legato/gen/voice/v1"; 8 | 9 | // Data containing all the necessary information to 10 | // create a consumer for a user in a voice channel 11 | // 12 | // This corresponds to https://mediasoup.org/documentation/v3/mediasoup-client/api/#ConsumerOptions on client: 13 | // - `consumer_id` -> `id` 14 | // - `producer_id` -> `producerId` 15 | // - `rtp_parameters` -> `rtpParameters` 16 | // - and `kind` should be set to "audio". 17 | message UserConsumerOptions { 18 | // User ID of the user. 19 | uint64 user_id = 1; 20 | // Producer ID of the producer being consumed. 21 | string producer_id = 2; 22 | // Consumer ID for the user's producer consumer. 23 | string consumer_id = 3; 24 | // RTP paramaters for the user's audio track. Corresponds to `RtpParameters` in mediasoup's TypeScript API. 25 | string rtp_parameters = 4; 26 | } 27 | 28 | // Object containing all the necessary information about transport options required 29 | // from the server to establish transport connection on the client 30 | // 31 | // This corresponds to https://mediasoup.org/documentation/v3/mediasoup-client/api/#TransportOptions on client: 32 | // - `id` -> `id` 33 | // - `ice_parameters` -> `iceParameters` 34 | // - `dtls_parameters` -> `dtlsParameters` 35 | // - `ice_candidates` -> `iceCandidates` 36 | message TransportOptions { 37 | // The transport ID. 38 | string id = 1; 39 | // DTLS paramaters in JSON. Corresponds to `DtlsParameters` in mediasoup's TypeScript API. 40 | string dtls_parameters = 2; 41 | // ICE candidates in JSON. Corresponds to `IceCandidate` in mediasoup's TypeScript API. 42 | repeated string ice_candidates = 3; 43 | // ICE paramaters in JSON. Corresponds to `IceParameters` in mediasoup's TypeScript API. 44 | string ice_parameters = 4; 45 | } 46 | 47 | // Used in `StreamMessage` endpoint. 48 | message StreamMessageRequest { 49 | // IDs that will be used to know which channel this WS will operate in 50 | message Initialize { 51 | // Guild ID of the guild where the channel is. 52 | uint64 guild_id = 1; 53 | // Channel ID of the voice channel to initialize for. 54 | uint64 channel_id = 2; 55 | } 56 | 57 | // Data needed to prepare for joining a channel 58 | message PrepareForJoinChannel { 59 | // RTP capabilities in JSON. 60 | string rtp_capabilities = 1; 61 | } 62 | 63 | // Data needed to join a channel 64 | // 65 | // This takes one RTP paramaters for one track, which will be 66 | // assumed to be Audio 67 | // 68 | // It also takes DTLS paramaters for connecting both producer and consumer 69 | message JoinChannel { 70 | // RTP paramaters in JSON. Corresponds to `RtpParameters` in mediasoup's TypeScript API. 71 | string rtp_paramaters = 1; 72 | // DTLS paramaters for producer transport, in JSON. Corresponds to `DtlsParameters` in mediasoup's TypeScript API. 73 | string producer_dtls_paramaters = 2; 74 | // DTLS paramaters for consumer transport, in JSON. Corresponds to `DtlsParameters` in mediasoup's TypeScript API. 75 | string consumer_dtls_paramaters = 3; 76 | } 77 | 78 | // Message to resume a consumer 79 | message ResumeConsumer { 80 | // ID of the consumer to resume. 81 | string consumer_id = 1; 82 | } 83 | 84 | // Message for this response 85 | oneof message { 86 | // Sent to initialize the WS and receive necessary information 87 | Initialize initialize = 1; 88 | // Sent to prepare for joining channel 89 | PrepareForJoinChannel prepare_for_join_channel = 2; 90 | // Sent to join a channel 91 | JoinChannel join_channel = 3; 92 | // Sent to resume a consumer 93 | ResumeConsumer resume_consumer = 4; 94 | } 95 | } 96 | // Used in `StreamMessage` endpoint. 97 | message StreamMessageResponse { 98 | // Initialization data for client 99 | message Initialized { 100 | // Server RTP capabilities in JSON. Corresponds to `RtpCapabilities` in mediasoup's TypeScript API. 101 | string rtp_capabilities = 1; 102 | } 103 | 104 | // RTP capabilities validated 105 | message PreparedForJoinChannel { 106 | // Consumer transport options 107 | TransportOptions consumer_transport_options = 1; 108 | // Producer transport options 109 | TransportOptions producer_transport_options = 2; 110 | } 111 | 112 | // Producer for voice created; consumer and producer transports are connected 113 | message JoinedChannel { 114 | // Consumer options for users that were already in the room 115 | repeated UserConsumerOptions other_users = 1; 116 | } 117 | 118 | // Data for the user that joined the room and it's producer 119 | message UserJoined { 120 | // Consumer options for this user 121 | UserConsumerOptions data = 1; 122 | } 123 | 124 | // Data for the user that left the room and the producer 125 | message UserLeft { 126 | // ID of the user that left 127 | uint64 user_id = 1; 128 | } 129 | 130 | // Message for this response 131 | oneof message { 132 | // Sent when connection is started 133 | Initialized initialized = 1; 134 | // Sent when preparing to join a channel is successful 135 | PreparedForJoinChannel prepared_for_join_channel = 2; 136 | // Sent when joining a channel is successful 137 | JoinedChannel joined_channel = 3; 138 | // Sent when another user joins the channel 139 | UserJoined user_joined = 4; 140 | // Sent when another user leaves the channel 141 | UserLeft user_left = 5; 142 | } 143 | } 144 | 145 | // Harmony service for facilitating voice operations using WebRTC. 146 | // 147 | // # Usage (for client) 148 | // 149 | // 0. Call StreamMessage to be able to send RTC commands to server 150 | // 1. Send Initialize over stream with guild_id and channel_id of the request set to the channel you want to join 151 | // 2. Init device by using the RTP capabilities sent in the response message, which should be Initialized 152 | // 3. Send PrepareForJoinChannel over stream with client rtp capabilities 153 | // 4. Wait for PreparedForJoinChannel, which contains transport options 154 | // 5. Connect both transports using the transport options on client 155 | // 6. Send JoinChannel over stream containing RTP paramaters for your Audio track 156 | // and DTLS paramaters for both consumer and producer 157 | // 7. Wait for JoinedChannel, which confirms you have successfully joined the voice channel; 158 | // handle other_users which will be described in 8 (UserJoined handling) 159 | // 8. Handle UserJoined and UserLeft events appropiately 160 | // - For UserJoined; use the received consumer ID, producer ID and RTP parameters on your 161 | // consumer transport to consume the producer, afterwards send ResumeConsumer message 162 | // with the consumer ID, then if that's successful add the track to a `user ID -> Track` map 163 | // - For UserLeft, remove the user track from the `user ID -> Track` map 164 | // 165 | // ## How this looks for servers 166 | // 167 | // 0. Receives StreamMessage, starts the socket 168 | // 1. Waits for Initialize 169 | // 2. Sends Initialized over stream with it's RTP capabilities 170 | // 3. Receives PrepareForJoinChannel with client RTP capabilities 171 | // 4. Sends PreparedForJoinChannel over stream with consumer and producer transport options 172 | // 5. Receives JoinChannel, checks for DTLS parameters for consumer and producer transports 173 | // and uses the RTP paramaters to create a producer for the client 174 | // 6. Sends JoinedChannel over stream with the created producer ID and all other users' data (UserData) 175 | // 7. When another user does 1 to 7, sends UserJoined over stream to all other users; 176 | // when a user leaves the channel (when their stream ends), sends UserLeft to all other users 177 | // 8. When receiving a ResumeConsumer message, unpauses the consumer corresponding to the consumer ID 178 | // 179 | service VoiceService { 180 | // Endpoint to stream messages between client and server. 181 | // 182 | // - One StreamMessage stream corresponds to being in one voice channel. 183 | // - It's recommended that users should not be able to be in more than one voice channel, 184 | // but this limitation is left up to the server implementation. 185 | rpc StreamMessage(stream StreamMessageRequest) returns (stream StreamMessageResponse) { 186 | option (harmonytypes.v1.metadata).requires_authentication = true; 187 | } 188 | } 189 | --------------------------------------------------------------------------------