mirror of
https://github.com/openai/openai-go.git
synced 2026-04-01 00:57:11 +09:00
feat(api): custom voices
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
configured_endpoints: 139
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-3e207c26eea3b15837c78ef2fe0e1c68937708fd0763971ce749c0bdb7db6376.yml
|
||||
openapi_spec_hash: 626982004d5a594a822fa7883422efb4
|
||||
config_hash: 0dda4b3af379312c9c55467a5e1e1ec0
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-cb3e4451108eed58d59cff25bf77ec0dc960ec9c6f3dba68f90e7a9847c09d21.yml
|
||||
openapi_spec_hash: dec6d9be64a5ba8f474a1f2a7a4fafef
|
||||
config_hash: e922f01e25accd07d8fd3641c37fbd62
|
||||
|
||||
@@ -62,10 +62,11 @@ type AudioSpeechNewParams struct {
|
||||
Model SpeechModel `json:"model,omitzero" api:"required"`
|
||||
// The voice to use when generating the audio. Supported built-in voices are
|
||||
// `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`,
|
||||
// `shimmer`, `verse`, `marin`, and `cedar`. Previews of the voices are available
|
||||
// in the
|
||||
// `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice
|
||||
// object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the
|
||||
// voices are available in the
|
||||
// [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).
|
||||
Voice AudioSpeechNewParamsVoice `json:"voice,omitzero" api:"required"`
|
||||
Voice AudioSpeechNewParamsVoiceUnion `json:"voice,omitzero" api:"required"`
|
||||
// Control the voice of your generated audio with additional instructions. Does not
|
||||
// work with `tts-1` or `tts-1-hd`.
|
||||
Instructions param.Opt[string] `json:"instructions,omitzero"`
|
||||
@@ -93,26 +94,68 @@ func (r *AudioSpeechNewParams) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
// The voice to use when generating the audio. Supported built-in voices are
|
||||
// `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`,
|
||||
// `shimmer`, `verse`, `marin`, and `cedar`. Previews of the voices are available
|
||||
// in the
|
||||
// [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).
|
||||
type AudioSpeechNewParamsVoice string
|
||||
// Only one field can be non-zero.
|
||||
//
|
||||
// Use [param.IsOmitted] to confirm if a field is set.
|
||||
type AudioSpeechNewParamsVoiceUnion struct {
|
||||
OfString param.Opt[string] `json:",omitzero,inline"`
|
||||
// Check if union is this variant with
|
||||
// !param.IsOmitted(union.OfAudioSpeechNewsVoiceString)
|
||||
OfAudioSpeechNewsVoiceString param.Opt[string] `json:",omitzero,inline"`
|
||||
OfAudioSpeechNewsVoiceID *AudioSpeechNewParamsVoiceID `json:",omitzero,inline"`
|
||||
paramUnion
|
||||
}
|
||||
|
||||
func (u AudioSpeechNewParamsVoiceUnion) MarshalJSON() ([]byte, error) {
|
||||
return param.MarshalUnion(u, u.OfString, u.OfAudioSpeechNewsVoiceString, u.OfAudioSpeechNewsVoiceID)
|
||||
}
|
||||
func (u *AudioSpeechNewParamsVoiceUnion) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, u)
|
||||
}
|
||||
|
||||
func (u *AudioSpeechNewParamsVoiceUnion) asAny() any {
|
||||
if !param.IsOmitted(u.OfString) {
|
||||
return &u.OfString.Value
|
||||
} else if !param.IsOmitted(u.OfAudioSpeechNewsVoiceString) {
|
||||
return &u.OfAudioSpeechNewsVoiceString
|
||||
} else if !param.IsOmitted(u.OfAudioSpeechNewsVoiceID) {
|
||||
return u.OfAudioSpeechNewsVoiceID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AudioSpeechNewParamsVoiceString string
|
||||
|
||||
const (
|
||||
AudioSpeechNewParamsVoiceAlloy AudioSpeechNewParamsVoice = "alloy"
|
||||
AudioSpeechNewParamsVoiceAsh AudioSpeechNewParamsVoice = "ash"
|
||||
AudioSpeechNewParamsVoiceBallad AudioSpeechNewParamsVoice = "ballad"
|
||||
AudioSpeechNewParamsVoiceCoral AudioSpeechNewParamsVoice = "coral"
|
||||
AudioSpeechNewParamsVoiceEcho AudioSpeechNewParamsVoice = "echo"
|
||||
AudioSpeechNewParamsVoiceSage AudioSpeechNewParamsVoice = "sage"
|
||||
AudioSpeechNewParamsVoiceShimmer AudioSpeechNewParamsVoice = "shimmer"
|
||||
AudioSpeechNewParamsVoiceVerse AudioSpeechNewParamsVoice = "verse"
|
||||
AudioSpeechNewParamsVoiceMarin AudioSpeechNewParamsVoice = "marin"
|
||||
AudioSpeechNewParamsVoiceCedar AudioSpeechNewParamsVoice = "cedar"
|
||||
AudioSpeechNewParamsVoiceStringAlloy AudioSpeechNewParamsVoiceString = "alloy"
|
||||
AudioSpeechNewParamsVoiceStringAsh AudioSpeechNewParamsVoiceString = "ash"
|
||||
AudioSpeechNewParamsVoiceStringBallad AudioSpeechNewParamsVoiceString = "ballad"
|
||||
AudioSpeechNewParamsVoiceStringCoral AudioSpeechNewParamsVoiceString = "coral"
|
||||
AudioSpeechNewParamsVoiceStringEcho AudioSpeechNewParamsVoiceString = "echo"
|
||||
AudioSpeechNewParamsVoiceStringSage AudioSpeechNewParamsVoiceString = "sage"
|
||||
AudioSpeechNewParamsVoiceStringShimmer AudioSpeechNewParamsVoiceString = "shimmer"
|
||||
AudioSpeechNewParamsVoiceStringVerse AudioSpeechNewParamsVoiceString = "verse"
|
||||
AudioSpeechNewParamsVoiceStringMarin AudioSpeechNewParamsVoiceString = "marin"
|
||||
AudioSpeechNewParamsVoiceStringCedar AudioSpeechNewParamsVoiceString = "cedar"
|
||||
)
|
||||
|
||||
// Custom voice reference.
|
||||
//
|
||||
// The property ID is required.
|
||||
type AudioSpeechNewParamsVoiceID struct {
|
||||
// The custom voice ID, e.g. `voice_1234`.
|
||||
ID string `json:"id" api:"required"`
|
||||
paramObj
|
||||
}
|
||||
|
||||
func (r AudioSpeechNewParamsVoiceID) MarshalJSON() (data []byte, err error) {
|
||||
type shadow AudioSpeechNewParamsVoiceID
|
||||
return param.MarshalObject(r, (*shadow)(&r))
|
||||
}
|
||||
func (r *AudioSpeechNewParamsVoiceID) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
// The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`,
|
||||
// `wav`, and `pcm`.
|
||||
type AudioSpeechNewParamsResponseFormat string
|
||||
|
||||
@@ -27,9 +27,11 @@ func TestAudioSpeechNewWithOptionalParams(t *testing.T) {
|
||||
option.WithAPIKey("My API Key"),
|
||||
)
|
||||
resp, err := client.Audio.Speech.New(context.TODO(), openai.AudioSpeechNewParams{
|
||||
Input: "input",
|
||||
Model: openai.SpeechModelTTS1,
|
||||
Voice: openai.AudioSpeechNewParamsVoiceAsh,
|
||||
Input: "input",
|
||||
Model: openai.SpeechModelTTS1,
|
||||
Voice: openai.AudioSpeechNewParamsVoiceUnion{
|
||||
OfString: openai.String("string"),
|
||||
},
|
||||
Instructions: openai.String("instructions"),
|
||||
ResponseFormat: openai.AudioSpeechNewParamsResponseFormatMP3,
|
||||
Speed: openai.Float(0.25),
|
||||
|
||||
@@ -545,8 +545,9 @@ type ChatCompletionAudioParam struct {
|
||||
Format ChatCompletionAudioParamFormat `json:"format,omitzero" api:"required"`
|
||||
// The voice the model uses to respond. Supported built-in voices are `alloy`,
|
||||
// `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`,
|
||||
// `marin`, and `cedar`.
|
||||
Voice ChatCompletionAudioParamVoice `json:"voice,omitzero" api:"required"`
|
||||
// `marin`, and `cedar`. You may also provide a custom voice object with an `id`,
|
||||
// for example `{ "id": "voice_1234" }`.
|
||||
Voice ChatCompletionAudioParamVoiceUnion `json:"voice,omitzero" api:"required"`
|
||||
paramObj
|
||||
}
|
||||
|
||||
@@ -571,24 +572,68 @@ const (
|
||||
ChatCompletionAudioParamFormatPcm16 ChatCompletionAudioParamFormat = "pcm16"
|
||||
)
|
||||
|
||||
// The voice the model uses to respond. Supported built-in voices are `alloy`,
|
||||
// `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`,
|
||||
// `marin`, and `cedar`.
|
||||
type ChatCompletionAudioParamVoice string
|
||||
// Only one field can be non-zero.
|
||||
//
|
||||
// Use [param.IsOmitted] to confirm if a field is set.
|
||||
type ChatCompletionAudioParamVoiceUnion struct {
|
||||
OfString param.Opt[string] `json:",omitzero,inline"`
|
||||
// Check if union is this variant with
|
||||
// !param.IsOmitted(union.OfChatCompletionAudioVoiceString)
|
||||
OfChatCompletionAudioVoiceString param.Opt[string] `json:",omitzero,inline"`
|
||||
OfChatCompletionAudioVoiceID *ChatCompletionAudioParamVoiceID `json:",omitzero,inline"`
|
||||
paramUnion
|
||||
}
|
||||
|
||||
func (u ChatCompletionAudioParamVoiceUnion) MarshalJSON() ([]byte, error) {
|
||||
return param.MarshalUnion(u, u.OfString, u.OfChatCompletionAudioVoiceString, u.OfChatCompletionAudioVoiceID)
|
||||
}
|
||||
func (u *ChatCompletionAudioParamVoiceUnion) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, u)
|
||||
}
|
||||
|
||||
func (u *ChatCompletionAudioParamVoiceUnion) asAny() any {
|
||||
if !param.IsOmitted(u.OfString) {
|
||||
return &u.OfString.Value
|
||||
} else if !param.IsOmitted(u.OfChatCompletionAudioVoiceString) {
|
||||
return &u.OfChatCompletionAudioVoiceString
|
||||
} else if !param.IsOmitted(u.OfChatCompletionAudioVoiceID) {
|
||||
return u.OfChatCompletionAudioVoiceID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChatCompletionAudioParamVoiceString string
|
||||
|
||||
const (
|
||||
ChatCompletionAudioParamVoiceAlloy ChatCompletionAudioParamVoice = "alloy"
|
||||
ChatCompletionAudioParamVoiceAsh ChatCompletionAudioParamVoice = "ash"
|
||||
ChatCompletionAudioParamVoiceBallad ChatCompletionAudioParamVoice = "ballad"
|
||||
ChatCompletionAudioParamVoiceCoral ChatCompletionAudioParamVoice = "coral"
|
||||
ChatCompletionAudioParamVoiceEcho ChatCompletionAudioParamVoice = "echo"
|
||||
ChatCompletionAudioParamVoiceSage ChatCompletionAudioParamVoice = "sage"
|
||||
ChatCompletionAudioParamVoiceShimmer ChatCompletionAudioParamVoice = "shimmer"
|
||||
ChatCompletionAudioParamVoiceVerse ChatCompletionAudioParamVoice = "verse"
|
||||
ChatCompletionAudioParamVoiceMarin ChatCompletionAudioParamVoice = "marin"
|
||||
ChatCompletionAudioParamVoiceCedar ChatCompletionAudioParamVoice = "cedar"
|
||||
ChatCompletionAudioParamVoiceStringAlloy ChatCompletionAudioParamVoiceString = "alloy"
|
||||
ChatCompletionAudioParamVoiceStringAsh ChatCompletionAudioParamVoiceString = "ash"
|
||||
ChatCompletionAudioParamVoiceStringBallad ChatCompletionAudioParamVoiceString = "ballad"
|
||||
ChatCompletionAudioParamVoiceStringCoral ChatCompletionAudioParamVoiceString = "coral"
|
||||
ChatCompletionAudioParamVoiceStringEcho ChatCompletionAudioParamVoiceString = "echo"
|
||||
ChatCompletionAudioParamVoiceStringSage ChatCompletionAudioParamVoiceString = "sage"
|
||||
ChatCompletionAudioParamVoiceStringShimmer ChatCompletionAudioParamVoiceString = "shimmer"
|
||||
ChatCompletionAudioParamVoiceStringVerse ChatCompletionAudioParamVoiceString = "verse"
|
||||
ChatCompletionAudioParamVoiceStringMarin ChatCompletionAudioParamVoiceString = "marin"
|
||||
ChatCompletionAudioParamVoiceStringCedar ChatCompletionAudioParamVoiceString = "cedar"
|
||||
)
|
||||
|
||||
// Custom voice reference.
|
||||
//
|
||||
// The property ID is required.
|
||||
type ChatCompletionAudioParamVoiceID struct {
|
||||
// The custom voice ID, e.g. `voice_1234`.
|
||||
ID string `json:"id" api:"required"`
|
||||
paramObj
|
||||
}
|
||||
|
||||
func (r ChatCompletionAudioParamVoiceID) MarshalJSON() (data []byte, err error) {
|
||||
type shadow ChatCompletionAudioParamVoiceID
|
||||
return param.MarshalObject(r, (*shadow)(&r))
|
||||
}
|
||||
func (r *ChatCompletionAudioParamVoiceID) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
// Represents a streamed chunk of a chat completion response returned by the model,
|
||||
// based on the provided input.
|
||||
// [Learn more](https://platform.openai.com/docs/guides/streaming-responses).
|
||||
|
||||
@@ -38,7 +38,9 @@ func TestChatCompletionNewWithOptionalParams(t *testing.T) {
|
||||
Model: shared.ChatModelGPT5_4,
|
||||
Audio: openai.ChatCompletionAudioParam{
|
||||
Format: openai.ChatCompletionAudioParamFormatWAV,
|
||||
Voice: openai.ChatCompletionAudioParamVoiceAsh,
|
||||
Voice: openai.ChatCompletionAudioParamVoiceUnion{
|
||||
OfString: openai.String("string"),
|
||||
},
|
||||
},
|
||||
FrequencyPenalty: openai.Float(-2),
|
||||
FunctionCall: openai.ChatCompletionNewParamsFunctionCallUnion{
|
||||
|
||||
@@ -68,7 +68,9 @@ func TestCallAcceptWithOptionalParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Speed: openai.Float(0.25),
|
||||
Voice: realtime.RealtimeAudioConfigOutputVoiceAsh,
|
||||
Voice: realtime.RealtimeAudioConfigOutputVoiceUnionParam{
|
||||
OfString: openai.String("string"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Include: []string{"item.input_audio_transcription.logprobs"},
|
||||
|
||||
@@ -70,7 +70,9 @@ func TestClientSecretNewWithOptionalParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Speed: openai.Float(0.25),
|
||||
Voice: realtime.RealtimeAudioConfigOutputVoiceAsh,
|
||||
Voice: realtime.RealtimeAudioConfigOutputVoiceUnionParam{
|
||||
OfString: openai.String("string"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Include: []string{"item.input_audio_transcription.logprobs"},
|
||||
|
||||
@@ -222,9 +222,11 @@ type RealtimeAudioConfigOutputParam struct {
|
||||
Format RealtimeAudioFormatsUnionParam `json:"format,omitzero"`
|
||||
// The voice the model uses to respond. Supported built-in voices are `alloy`,
|
||||
// `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and
|
||||
// `cedar`. Voice cannot be changed during the session once the model has responded
|
||||
// with audio at least once. We recommend `marin` and `cedar` for best quality.
|
||||
Voice RealtimeAudioConfigOutputVoice `json:"voice,omitzero"`
|
||||
// `cedar`. You may also provide a custom voice object with an `id`, for example
|
||||
// `{ "id": "voice_1234" }`. Voice cannot be changed during the session once the
|
||||
// model has responded with audio at least once. We recommend `marin` and `cedar`
|
||||
// for best quality.
|
||||
Voice RealtimeAudioConfigOutputVoiceUnionParam `json:"voice,omitzero"`
|
||||
paramObj
|
||||
}
|
||||
|
||||
@@ -236,25 +238,68 @@ func (r *RealtimeAudioConfigOutputParam) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
// The voice the model uses to respond. Supported built-in voices are `alloy`,
|
||||
// `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and
|
||||
// `cedar`. Voice cannot be changed during the session once the model has responded
|
||||
// with audio at least once. We recommend `marin` and `cedar` for best quality.
|
||||
type RealtimeAudioConfigOutputVoice string
|
||||
// Only one field can be non-zero.
|
||||
//
|
||||
// Use [param.IsOmitted] to confirm if a field is set.
|
||||
type RealtimeAudioConfigOutputVoiceUnionParam struct {
|
||||
OfString param.Opt[string] `json:",omitzero,inline"`
|
||||
// Check if union is this variant with
|
||||
// !param.IsOmitted(union.OfRealtimeAudioConfigOutputVoiceString)
|
||||
OfRealtimeAudioConfigOutputVoiceString param.Opt[string] `json:",omitzero,inline"`
|
||||
OfRealtimeAudioConfigOutputVoiceID *RealtimeAudioConfigOutputVoiceIDParam `json:",omitzero,inline"`
|
||||
paramUnion
|
||||
}
|
||||
|
||||
func (u RealtimeAudioConfigOutputVoiceUnionParam) MarshalJSON() ([]byte, error) {
|
||||
return param.MarshalUnion(u, u.OfString, u.OfRealtimeAudioConfigOutputVoiceString, u.OfRealtimeAudioConfigOutputVoiceID)
|
||||
}
|
||||
func (u *RealtimeAudioConfigOutputVoiceUnionParam) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, u)
|
||||
}
|
||||
|
||||
func (u *RealtimeAudioConfigOutputVoiceUnionParam) asAny() any {
|
||||
if !param.IsOmitted(u.OfString) {
|
||||
return &u.OfString.Value
|
||||
} else if !param.IsOmitted(u.OfRealtimeAudioConfigOutputVoiceString) {
|
||||
return &u.OfRealtimeAudioConfigOutputVoiceString
|
||||
} else if !param.IsOmitted(u.OfRealtimeAudioConfigOutputVoiceID) {
|
||||
return u.OfRealtimeAudioConfigOutputVoiceID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RealtimeAudioConfigOutputVoiceString string
|
||||
|
||||
const (
|
||||
RealtimeAudioConfigOutputVoiceAlloy RealtimeAudioConfigOutputVoice = "alloy"
|
||||
RealtimeAudioConfigOutputVoiceAsh RealtimeAudioConfigOutputVoice = "ash"
|
||||
RealtimeAudioConfigOutputVoiceBallad RealtimeAudioConfigOutputVoice = "ballad"
|
||||
RealtimeAudioConfigOutputVoiceCoral RealtimeAudioConfigOutputVoice = "coral"
|
||||
RealtimeAudioConfigOutputVoiceEcho RealtimeAudioConfigOutputVoice = "echo"
|
||||
RealtimeAudioConfigOutputVoiceSage RealtimeAudioConfigOutputVoice = "sage"
|
||||
RealtimeAudioConfigOutputVoiceShimmer RealtimeAudioConfigOutputVoice = "shimmer"
|
||||
RealtimeAudioConfigOutputVoiceVerse RealtimeAudioConfigOutputVoice = "verse"
|
||||
RealtimeAudioConfigOutputVoiceMarin RealtimeAudioConfigOutputVoice = "marin"
|
||||
RealtimeAudioConfigOutputVoiceCedar RealtimeAudioConfigOutputVoice = "cedar"
|
||||
RealtimeAudioConfigOutputVoiceStringAlloy RealtimeAudioConfigOutputVoiceString = "alloy"
|
||||
RealtimeAudioConfigOutputVoiceStringAsh RealtimeAudioConfigOutputVoiceString = "ash"
|
||||
RealtimeAudioConfigOutputVoiceStringBallad RealtimeAudioConfigOutputVoiceString = "ballad"
|
||||
RealtimeAudioConfigOutputVoiceStringCoral RealtimeAudioConfigOutputVoiceString = "coral"
|
||||
RealtimeAudioConfigOutputVoiceStringEcho RealtimeAudioConfigOutputVoiceString = "echo"
|
||||
RealtimeAudioConfigOutputVoiceStringSage RealtimeAudioConfigOutputVoiceString = "sage"
|
||||
RealtimeAudioConfigOutputVoiceStringShimmer RealtimeAudioConfigOutputVoiceString = "shimmer"
|
||||
RealtimeAudioConfigOutputVoiceStringVerse RealtimeAudioConfigOutputVoiceString = "verse"
|
||||
RealtimeAudioConfigOutputVoiceStringMarin RealtimeAudioConfigOutputVoiceString = "marin"
|
||||
RealtimeAudioConfigOutputVoiceStringCedar RealtimeAudioConfigOutputVoiceString = "cedar"
|
||||
)
|
||||
|
||||
// Custom voice reference.
|
||||
//
|
||||
// The property ID is required.
|
||||
type RealtimeAudioConfigOutputVoiceIDParam struct {
|
||||
// The custom voice ID, e.g. `voice_1234`.
|
||||
ID string `json:"id" api:"required"`
|
||||
paramObj
|
||||
}
|
||||
|
||||
func (r RealtimeAudioConfigOutputVoiceIDParam) MarshalJSON() (data []byte, err error) {
|
||||
type shadow RealtimeAudioConfigOutputVoiceIDParam
|
||||
return param.MarshalObject(r, (*shadow)(&r))
|
||||
}
|
||||
func (r *RealtimeAudioConfigOutputVoiceIDParam) UnmarshalJSON(data []byte) error {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
// RealtimeAudioFormatsUnion contains all possible properties and values from
|
||||
// [RealtimeAudioFormatsAudioPCM], [RealtimeAudioFormatsAudioPCMU],
|
||||
// [RealtimeAudioFormatsAudioPCMA].
|
||||
|
||||
Reference in New Issue
Block a user