Use a trait to represent each endpoint.
This commit is contained in:
parent
11285ddc95
commit
c310cd1395
@ -14,3 +14,4 @@ version = "0.1.0"
|
||||
ruma-identifiers = "0.4.1"
|
||||
serde = "0.8.10"
|
||||
serde_derive = "0.8.10"
|
||||
serde_json = "0.8.2"
|
||||
|
43
src/lib.rs
43
src/lib.rs
@ -7,6 +7,9 @@
|
||||
extern crate ruma_identifiers;
|
||||
extern crate serde;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Endpoints for the r0.x.x versions of the client API specification.
|
||||
pub mod r0 {
|
||||
@ -36,4 +39,42 @@ pub mod r0 {
|
||||
}
|
||||
|
||||
/// GET /_matrix/client/versions
|
||||
pub mod version;
|
||||
pub mod supported_versions;
|
||||
|
||||
/// HTTP request methods used in Matrix APIs.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Method {
|
||||
/// DELETE
|
||||
Delete,
|
||||
/// GET
|
||||
Get,
|
||||
/// POST
|
||||
Post,
|
||||
/// PUT
|
||||
Put,
|
||||
}
|
||||
|
||||
/// An API endpoint.
|
||||
pub trait Endpoint {
|
||||
/// Request parameters supplied via the body of the HTTP request.
|
||||
type BodyParams: Deserialize + Serialize;
|
||||
|
||||
/// Request parameters supplied via the URL's path.
|
||||
type PathParams;
|
||||
|
||||
/// Parameters supplied via the URL's query string.
|
||||
type QueryParams;
|
||||
|
||||
/// The body of the response.
|
||||
type Response: Deserialize + Serialize;
|
||||
|
||||
/// Returns the HTTP method used by this endpoint.
|
||||
fn method() -> Method;
|
||||
|
||||
/// Generates the path component of the URL for this endpoint using the supplied parameters.
|
||||
fn request_path(params: Self::PathParams) -> String;
|
||||
|
||||
/// Generates a generic path component of the URL for this endpoint, suitable for `Router` from
|
||||
/// the router crate.
|
||||
fn router_path() -> String;
|
||||
}
|
||||
|
@ -2,11 +2,21 @@
|
||||
|
||||
/// POST /_matrix/client/r0/register
|
||||
pub mod register {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
/// This API endpoint's body parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BodyParams {
|
||||
pub bind_email: Option<bool>,
|
||||
pub password: String,
|
||||
pub username: Option<String>,
|
||||
}
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/register";
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// This API endpoint's query string parameters.
|
||||
pub struct QueryParams {
|
||||
pub kind: Option<RegistrationKind>,
|
||||
}
|
||||
|
||||
/// The kind of account being registered.
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
|
||||
@ -17,71 +27,145 @@ pub mod register {
|
||||
User,
|
||||
}
|
||||
|
||||
/// The request type.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Request {
|
||||
pub bind_email: Option<bool>,
|
||||
pub kind: Option<RegistrationKind>,
|
||||
pub password: String,
|
||||
pub username: Option<String>,
|
||||
}
|
||||
|
||||
/// The response type.
|
||||
/// This API endpoint's response.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
pub access_token: String,
|
||||
pub home_server: String,
|
||||
pub user_id: String,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = BodyParams;
|
||||
type PathParams = ();
|
||||
type QueryParams = QueryParams;
|
||||
type Response = Response;
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/register".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /_matrix/client/r0/account/password/email/requestToken
|
||||
pub mod request_password_change_token {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/account/password/email/requestToken";
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/account/password/email/requestToken".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /_matrix/client/r0/account/deactivate
|
||||
pub mod deactivate {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/account/deactivate";
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/account/deactivate".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /_matrix/client/r0/account/password
|
||||
pub mod change_password {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/account/password";
|
||||
|
||||
/// The request type.
|
||||
/// This API endpoint's body parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Request {
|
||||
pub struct BodyParams {
|
||||
pub new_password: String,
|
||||
}
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = BodyParams;
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/account/password".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /_matrix/client/r0/register/email/requestToken
|
||||
pub mod request_register_token {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/register/email/requestToken";
|
||||
|
||||
/// The request type.
|
||||
/// This API endpoint's body parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Request {
|
||||
pub struct BodyParams {
|
||||
pub client_secret: String,
|
||||
pub email: String,
|
||||
pub id_server: Option<String>,
|
||||
pub send_attempt: u64,
|
||||
}
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = BodyParams;
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/register/email/requestToken".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
101
src/r0/alias.rs
101
src/r0/alias.rs
@ -1 +1,102 @@
|
||||
//! Endpoints for room aliases.
|
||||
|
||||
use ruma_identifiers::RoomAlias;
|
||||
|
||||
/// PUT /_matrix/client/r0/directory/room/:room_alias
|
||||
pub mod create {
|
||||
use ruma_identifiers::RoomId;
|
||||
|
||||
/// This API endpoint's body parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BodyParams {
|
||||
pub room_id: RoomId,
|
||||
}
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = BodyParams;
|
||||
type PathParams = super::PathParams;
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Put
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
format!("/_matrix/client/r0/directory/room/{}", params.room_alias)
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/directory/room/:room_alias".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// DELETE /_matrix/client/r0/directory/room/:room_alias
|
||||
pub mod delete {
|
||||
use ruma_identifiers::RoomId;
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = super::PathParams;
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Delete
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
format!("/_matrix/client/r0/directory/room/{}", params.room_alias)
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/directory/room/:room_alias".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GET /_matrix/client/r0/directory/room/:room_alias
|
||||
pub mod get {
|
||||
use ruma_identifiers::RoomId;
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// This API endpoint's response.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
pub room_id: RoomId,
|
||||
pub servers: Vec<String>,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = super::PathParams;
|
||||
type QueryParams = ();
|
||||
type Response = Response;
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Get
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
format!("/_matrix/client/r0/directory/room/{}", params.room_alias)
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/directory/room/:room_alias".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// These API endpoints' path parameters.
|
||||
pub struct PathParams {
|
||||
pub room_alias: RoomAlias,
|
||||
}
|
||||
|
@ -1 +1,77 @@
|
||||
//! Endpoints for client configuration.
|
||||
|
||||
/// PUT /_matrix/client/r0/user/:user_id/rooms/:room_id/account_data/:type
|
||||
pub mod set_room_account_data {
|
||||
use ruma_identifiers::{RoomId, UserId};
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// This API endpoint's path parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PathParams {
|
||||
pub user_id: UserId,
|
||||
pub room_id: RoomId,
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ::serde_json::Value;
|
||||
type PathParams = PathParams;
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Put
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
format!(
|
||||
"/_matrix/client/r0/user/{}/rooms/{}/account_data/{}",
|
||||
params.user_id,
|
||||
params.room_id,
|
||||
params.event_type
|
||||
)
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/user/:user_id/rooms/:room_id/account_data/:type".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PUT /_matrix/client/r0/user/:user_id/account_data/:type
|
||||
pub mod set_global_account_data {
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// This API endpoint's path parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PathParams {
|
||||
pub user_id: UserId,
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ::serde_json::Value;
|
||||
type PathParams = PathParams;
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Put
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
format!(
|
||||
"/_matrix/client/r0/user/{}/account_data/{}",
|
||||
params.user_id,
|
||||
params.event_type
|
||||
)
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/user/:user_id/account_data/:type".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,22 +4,9 @@
|
||||
pub mod create_room {
|
||||
use ruma_identifiers::RoomId;
|
||||
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/createRoom";
|
||||
|
||||
/// Extra options to be added to the `m.room.create` event.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct CreationContent {
|
||||
#[serde(rename="m.federate")]
|
||||
pub federate: Option<bool>,
|
||||
}
|
||||
|
||||
/// The request type.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Request {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BodyParams {
|
||||
pub creation_content: Option<CreationContent>,
|
||||
pub invite: Option<Vec<String>>,
|
||||
pub name: Option<String>,
|
||||
@ -29,14 +16,24 @@ pub mod create_room {
|
||||
pub visibility: Option<String>,
|
||||
}
|
||||
|
||||
/// Extra options to be added to the `m.room.create` event.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CreationContent {
|
||||
#[serde(rename="m.federate")]
|
||||
pub federate: Option<bool>,
|
||||
}
|
||||
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// The response type.
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
room_id: RoomId,
|
||||
pub room_id: RoomId,
|
||||
}
|
||||
|
||||
/// A convenience parameter for setting a few default state events.
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum RoomPreset {
|
||||
/// `join_rules` is set to `invite` and `history_visibility` is set to `shared`.
|
||||
#[serde(rename="private_chat")]
|
||||
@ -48,4 +45,23 @@ pub mod create_room {
|
||||
#[serde(rename="trusted_private_chat")]
|
||||
TrustedPrivateChat,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = BodyParams;
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = Response;
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/createRoom".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,10 @@
|
||||
|
||||
/// POST /_matrix/client/r0/login
|
||||
pub mod login {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/login";
|
||||
|
||||
/// The response type.
|
||||
/// This API endpoint's response.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
pub access_token: String,
|
||||
@ -16,35 +13,86 @@ pub mod login {
|
||||
pub refresh_token: Option<String>,
|
||||
pub user_id: String,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = Response;
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/login".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /_matrix/client/r0/logout
|
||||
pub mod logout {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/logout";
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = ();
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/logout".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// POST /_matrix/client/r0/tokenrefresh
|
||||
pub mod refresh_access_token {
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "POST";
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/_matrix/client/r0/tokenrefresh";
|
||||
|
||||
/// The request type.
|
||||
/// This API endpoint's body parameters.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Request {
|
||||
pub struct BodyParams {
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
/// The response type.
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// This API endpoint's response.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
pub access_token: String,
|
||||
pub refresh_token: Option<String>,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = BodyParams;
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = Response;
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Post
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/r0/tokenrefresh".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
28
src/supported_versions.rs
Normal file
28
src/supported_versions.rs
Normal file
@ -0,0 +1,28 @@
|
||||
/// Details about this API endpoint.
|
||||
pub struct Endpoint;
|
||||
|
||||
/// This API endpoint's response.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
/// A list of Matrix client API protocol versions supported by the homeserver.
|
||||
pub versions: Vec<String>,
|
||||
}
|
||||
|
||||
impl ::Endpoint for Endpoint {
|
||||
type BodyParams = ();
|
||||
type PathParams = ();
|
||||
type QueryParams = ();
|
||||
type Response = Response;
|
||||
|
||||
fn method() -> ::Method {
|
||||
::Method::Get
|
||||
}
|
||||
|
||||
fn request_path(params: Self::PathParams) -> String {
|
||||
Self::router_path()
|
||||
}
|
||||
|
||||
fn router_path() -> String {
|
||||
"/_matrix/client/versions".to_string()
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
/// The HTTP method.
|
||||
pub const METHOD: &'static str = "GET";
|
||||
|
||||
/// The URL's path component.
|
||||
pub const PATH: &'static str = "/versions";
|
||||
|
||||
/// The response type.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Response {
|
||||
/// A list of Matrix client API protocol versions supported by the homeserver.
|
||||
pub versions: Vec<String>,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user