syntax = "proto3"; package chatto.api.v1; import "google/protobuf/field_mask.proto"; import "buf/validate/validate.proto"; import "chatto/api/v1/common.proto"; import "chatto/api/v1/member_directory.proto"; import "chatto/api/v1/pagination.proto"; import "chatto/api/v1/users.proto"; // Request for users visible to the authenticated user. // Results use creation time, oldest first. Users without a creation time // appear last, ordered by case-insensitive login. message ListUsersRequest { reserved 2, 3; reserved "limit", "offset"; // Optional case-insensitive search against login and display name. string search = 1 [(buf.validate.field).string.max_len = 100]; // Defaults to 20 results when absent or limit is zero. Maximum: 500. PageRequest page = 4; } // User directory page. message ListUsersResponse { reserved 2, 3; reserved "total_count", "has_more"; // Users in the requested page. repeated DirectoryMember users = 1; // Page metadata. PageInfo page = 4; } // Request one visible user by stable user ID or login. message GetUserRequest { oneof target { // Target stable user ID. string user_id = 1 [(buf.validate.field).string.min_len = 1]; // Target login identifier. string login = 2 [(buf.validate.field).string.min_len = 1]; } } // User directory response. message GetUserResponse { // Requested user. DirectoryMember user = 1; } // Request user directory rows for a set of stable user IDs. message BatchGetUsersRequest { // Required target user IDs. Unknown IDs are omitted from the response. repeated string user_ids = 1 [(buf.validate.field).repeated = { min_items: 1 max_items: 100 items: { string: {min_len: 1} } }]; } // Batch user directory response. message BatchGetUsersResponse { // Found users. The server preserves first-seen request order and // de-duplicates repeated IDs. repeated DirectoryMember users = 1; } // Request to update one user's public profile. update_mask selects fields // to apply or reset. Fields outside the mask stay unchanged. message UpdateUserProfileRequest { // Required target user ID. Self-updates are allowed. Updating another user // requires the applicable account-management or bot-management authority. string user_id = 1 [(buf.validate.field).string.min_len = 1]; // New non-empty display name. The server also rejects control and // confusing invisible characters. optional string display_name = 2 [(buf.validate.field).string = {min_len: 1, max_len: 32}]; // New login identifier, when changing it. The server accepts ASCII letters, // digits, period, underscore, and hyphen, starting with a letter or digit. optional string login = 3 [(buf.validate.field).string = { min_len: 2 max_len: 32 }]; // New Markdown biography, when changing it. Empty clears the bio. optional string bio = 4 [(buf.validate.field).string.max_len = 1000]; // Editable fields to apply or reset: display_name, login, bio. // Omit to infer populated fields; * selects all editable fields. An explicit // empty mask is invalid. Unselected values are ignored. Selected absent values // reset the field to its default, subject to field validation. google.protobuf.FieldMask update_mask = 5; } // Result of a user profile update. message UpdateUserProfileResponse { // Updated user profile. User user = 1; } // Request to upload and set one user's avatar. message UploadAvatarRequest { reserved 1 to 3; reserved "filename", "content_type"; // Image payload. The server validates, resizes, and stores a WebP avatar. ImageUpload image = 4; // Required target user ID. Self-updates are allowed. Updating another user // requires the applicable account-management or bot-management authority. string user_id = 5 [(buf.validate.field).string.min_len = 1]; } // Result of uploading one user's avatar. message UploadAvatarResponse { // Updated user profile. User user = 1; } // Request to delete one user's avatar. message DeleteAvatarRequest { // Required target user ID. Self-updates are allowed. Updating another user // requires the applicable account-management or bot-management authority. string user_id = 1 [(buf.validate.field).string.min_len = 1]; } // Result of deleting one user's avatar. message DeleteAvatarResponse { // Updated user profile. User user = 1; } // Reads users and manages public user profiles and avatars. service UserService { // Lists users visible to the authenticated user. Admin-sensitive account // fields stay out of this public row shape. rpc ListUsers(ListUsersRequest) returns (ListUsersResponse); // Gets one visible user by user ID or login. Returns NOT_FOUND when the // target is unknown. rpc GetUser(GetUserRequest) returns (GetUserResponse); // Gets visible user rows for multiple stable user IDs. Unknown IDs are // omitted from the response. rpc BatchGetUsers(BatchGetUsersRequest) returns (BatchGetUsersResponse); // Updates the target user's login, display name, and/or bio. Users can // update themselves. Updating another human requires user.manage-accounts. // A human who updates a bot needs ownership of the bot, // user.manage-accounts, or bot.manage. A bot cannot target another account. // A login change returns FAILED_PRECONDITION while the target's // username-change cooldown is active. A successful login change starts a // new cooldown. This also applies when a bot owner or bot manager changes a // bot's login. A caller with user.manage-accounts bypasses the cooldown and // does not start it. A case-only change does not check or start the // cooldown. Unknown or deleted targets return NOT_FOUND. An invalid ID or a // request without a selected field returns INVALID_ARGUMENT. rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UpdateUserProfileResponse); // Uploads and sets an avatar for the target user. Users can update // themselves. Updating another human requires user.manage-accounts. // A human who updates a bot needs ownership of the bot, // user.manage-accounts, or bot.manage. // A bot cannot target another account. Unknown or deleted targets return // NOT_FOUND. Invalid IDs or missing images return INVALID_ARGUMENT. rpc UploadAvatar(UploadAvatarRequest) returns (UploadAvatarResponse); // Deletes the target user's avatar. Authorization matches UploadAvatar. The // call is idempotent. Unknown or deleted targets return NOT_FOUND. An // invalid ID returns INVALID_ARGUMENT. rpc DeleteAvatar(DeleteAvatarRequest) returns (DeleteAvatarResponse) { option idempotency_level = IDEMPOTENT; } }