var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false);
var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false);
var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this).ConfigureAwait(false);
var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this, services).ConfigureAwait(false);
foreach (var info in moduleDefs)
foreach (var info in moduleDefs)
{
{
@@ -196,10 +215,11 @@ namespace Discord.Commands
return true;
return true;
}
}
//Type Readers
//Type Readers
/// <summary>
/// <summary>
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
/// If <typeparamref name="T"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> will also be added.
/// If <typeparamref name="T"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> will also be added.
/// If a default <see cref="TypeReader"/> exists for <typeparamref name="T"/>, a warning will be logged and the default <see cref="TypeReader"/> will be replaced.
/// </summary>
/// </summary>
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
@@ -207,24 +227,61 @@ namespace Discord.Commands
=> AddTypeReader(typeof(T), reader);
=> AddTypeReader(typeof(T), reader);
/// <summary>
/// <summary>
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
/// If <paramref name="type"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> for the value type will also be added.
/// If <paramref name="type"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> for the value type will also be added.
/// If a default <see cref="TypeReader"/> exists for <paramref name="type"/>, a warning will be logged and the default <see cref="TypeReader"/> will be replaced.
/// </summary>
/// </summary>
/// <param name="type">A <see cref="Type"/> instance for the type to be read.</param>
/// <param name="type">A <see cref="Type"/> instance for the type to be read.</param>
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
public void AddTypeReader(Type type, TypeReader reader)
public void AddTypeReader(Type type, TypeReader reader)
{
{
var readers = _typeReaders.GetOrAdd(type, x => new ConcurrentDictionary<Type, TypeReader>());
readers[reader.GetType()] = reader;
if (_defaultTypeReaders.ContainsKey(type))
_ = _cmdLogger.WarningAsync($"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." +
$"To suppress this message, use AddTypeReader<T>(reader, true).");
AddTypeReader(type, reader, true);
}
/// <summary>
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
/// If <typeparamref name="T"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> will also be added.
/// </summary>
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
/// <param name="replaceDefault">If <paramref name="reader"/> should replace the default <see cref="TypeReader"/> for <typeparamref name="T"/> if one exists.</param>
public void AddTypeReader<T>(TypeReader reader, bool replaceDefault)
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
/// If <paramref name="type"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> for the value type will also be added.
/// </summary>
/// <param name="type">A <see cref="Type"/> instance for the type to be read.</param>
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
/// <param name="replaceDefault">If <paramref name="reader"/> should replace the default <see cref="TypeReader"/> for <paramref name="type"/> if one exists.</param>
public void AddTypeReader(Type type, TypeReader reader, bool replaceDefault)
{
if (replaceDefault && _defaultTypeReaders.ContainsKey(type))
public const int MaxEmbedLength = 6000; // user bot limit is 2000, but we don't validate that here.
public const int MaxEmbedLength = 6000;
public EmbedBuilder()
public EmbedBuilder()
{
{
_embed = new Embed(EmbedType.Rich);
Fields = new List<EmbedFieldBuilder>();
Fields = new List<EmbedFieldBuilder>();
}
}
public string Title
public string Title
{
{
get => _embed.Title;
get => _title;
set
set
{
{
if (value?.Length > MaxTitleLength) throw new ArgumentException($"Title length must be less than or equal to {MaxTitleLength}.", nameof(Title));
if (value?.Length > MaxTitleLength) throw new ArgumentException($"Title length must be less than or equal to {MaxTitleLength}.", nameof(Title));
_embed.Title = value;
_title = value;
}
}
}
}
public string Description
public string Description
{
{
get => _embed.Description;
get => _description;
set
set
{
{
if (value?.Length > MaxDescriptionLength) throw new ArgumentException($"Description length must be less than or equal to {MaxDescriptionLength}.", nameof(Description));
if (value?.Length > MaxDescriptionLength) throw new ArgumentException($"Description length must be less than or equal to {MaxDescriptionLength}.", nameof(Description));
_embed.Description = value;
_description = value;
}
}
}
}
public string Url
public string Url
{
{
get => _embed.Url;
get => _url;
set
set
{
{
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(Url));
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(Url));
_embed.Url = value;
_url = value;
}
}
}
}
public string ThumbnailUrl
public string ThumbnailUrl
{
{
get => _embed.Thumbnail?.Url;
get => _thumbnail?.Url;
set
set
{
{
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(ThumbnailUrl));
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(ThumbnailUrl));
_embed.Thumbnail = new EmbedThumbnail(value, null, null, null);
_thumbnail = new EmbedThumbnail(value, null, null, null);
}
}
}
}
public string ImageUrl
public string ImageUrl
{
{
get => _embed.Image?.Url;
get => _image?.Url;
set
set
{
{
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(ImageUrl));
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(ImageUrl));
_embed.Image = new EmbedImage(value, null, null, null);
_image = new EmbedImage(value, null, null, null);
}
}
}
}
public DateTimeOffset? Timestamp { get => _embed.Timestamp; set { _embed.Timestamp = value; } }
public Color? Color { get => _embed.Color; set { _embed.Color = value; } }
public EmbedAuthorBuilder Author { get; set; }
public EmbedFooterBuilder Footer { get; set; }
private List<EmbedFieldBuilder> _fields;
public List<EmbedFieldBuilder> Fields
public List<EmbedFieldBuilder> Fields
{
{
get => _fields;
get => _fields;
set
set
{
{
if (value == null) throw new ArgumentNullException("Cannot set an embed builder's fields collection to null", nameof(Fields));
if (value == null) throw new ArgumentNullException("Cannot set an embed builder's fields collection to null", nameof(Fields));
if (value.Count > MaxFieldCount) throw new ArgumentException($"Field count must be less than or equal to {MaxFieldCount}.", nameof(Fields));
if (value.Count > MaxFieldCount) throw new ArgumentException($"Field count must be less than or equal to {MaxFieldCount}.", nameof(Fields));
_fields = value;
_fields = value;
}
}
}
}
public DateTimeOffset? Timestamp { get; set; }
public Color? Color { get; set; }
public EmbedAuthorBuilder Author { get; set; }
public EmbedFooterBuilder Footer { get; set; }
public int Length
{
get
{
int titleLength = Title?.Length ?? 0;
int authorLength = Author?.Name?.Length ?? 0;
int descriptionLength = Description?.Length ?? 0;
int footerLength = Footer?.Text?.Length ?? 0;
int fieldSum = Fields.Sum(f => f.Name.Length + f.Value.ToString().Length);
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException($"Field name must not be null, empty or entirely whitespace.", nameof(Name));
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException($"Field name must not be null, empty or entirely whitespace.", nameof(Name));
if (value.Length > MaxFieldNameLength) throw new ArgumentException($"Field name length must be less than or equal to {MaxFieldNameLength}.", nameof(Name));
if (value.Length > MaxFieldNameLength) throw new ArgumentException($"Field name length must be less than or equal to {MaxFieldNameLength}.", nameof(Name));
_field.Name = value;
_name = value;
}
}
}
}
public object Value
public object Value
{
{
get => _field.Value;
get => _value;
set
set
{
{
var stringValue = value?.ToString();
var stringValue = value?.ToString();
if (string.IsNullOrEmpty(stringValue)) throw new ArgumentException($"Field value must not be null or empty.", nameof(Value));
if (string.IsNullOrEmpty(stringValue)) throw new ArgumentException($"Field value must not be null or empty.", nameof(Value));
if (stringValue.Length > MaxFieldValueLength) throw new ArgumentException($"Field value length must be less than or equal to {MaxFieldValueLength}.", nameof(Value));
if (stringValue.Length > MaxFieldValueLength) throw new ArgumentException($"Field value length must be less than or equal to {MaxFieldValueLength}.", nameof(Value));
_field.Value = stringValue;
_value = stringValue;
}
}
}
}
public bool IsInline { get => _field.Inline; set { _field.Inline = value; } }
public EmbedFieldBuilder()
{
_field = new EmbedField();
}
public bool IsInline { get; set; }
public EmbedFieldBuilder WithName(string name)
public EmbedFieldBuilder WithName(string name)
{
{
@@ -270,48 +276,44 @@ namespace Discord
}
}
public EmbedField Build()
public EmbedField Build()
=> _field;
=> new EmbedField(Name, Value.ToString(), IsInline);
}
}
public class EmbedAuthorBuilder
public class EmbedAuthorBuilder
{
{
private EmbedAuthor _author;
private string _name;
private string _url;
private string _iconUrl;
public const int MaxAuthorNameLength = 256;
public const int MaxAuthorNameLength = 256;
public string Name
public string Name
{
{
get => _author.Name;
get => _name;
set
set
{
{
if (value?.Length > MaxAuthorNameLength) throw new ArgumentException($"Author name length must be less than or equal to {MaxAuthorNameLength}.", nameof(Name));
if (value?.Length > MaxAuthorNameLength) throw new ArgumentException($"Author name length must be less than or equal to {MaxAuthorNameLength}.", nameof(Name));
_author.Name = value;
_name = value;
}
}
}
}
public string Url
public string Url
{
{
get => _author.Url;
get => _url;
set
set
{
{
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(Url));
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(Url));
_author.Url = value;
_url = value;
}
}
}
}
public string IconUrl
public string IconUrl
{
{
get => _author.IconUrl;
get => _iconUrl;
set
set
{
{
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(IconUrl));
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(IconUrl));
_author.IconUrl = value;
_iconUrl = value;
}
}
}
}
public EmbedAuthorBuilder()
{
_author = new EmbedAuthor();
}
public EmbedAuthorBuilder WithName(string name)
public EmbedAuthorBuilder WithName(string name)
{
{
Name = name;
Name = name;
@@ -329,39 +331,35 @@ namespace Discord
}
}
public EmbedAuthor Build()
public EmbedAuthor Build()
=> _author;
=> new EmbedAuthor(Name, Url, IconUrl, null);
}
}
public class EmbedFooterBuilder
public class EmbedFooterBuilder
{
{
private EmbedFooter _footer;
private string _text;
private string _iconUrl;
public const int MaxFooterTextLength = 2048;
public const int MaxFooterTextLength = 2048;
public string Text
public string Text
{
{
get => _footer.Text;
get => _text;
set
set
{
{
if (value?.Length > MaxFooterTextLength) throw new ArgumentException($"Footer text length must be less than or equal to {MaxFooterTextLength}.", nameof(Text));
if (value?.Length > MaxFooterTextLength) throw new ArgumentException($"Footer text length must be less than or equal to {MaxFooterTextLength}.", nameof(Text));
_footer.Text = value;
_text = value;
}
}
}
}
public string IconUrl
public string IconUrl
{
{
get => _footer.IconUrl;
get => _iconUrl;
set
set
{
{
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(IconUrl));
if (!value.IsNullOrUri()) throw new ArgumentException("Url must be a well-formed URI", nameof(IconUrl));
[Obsolete("User logins are being deprecated and may result in a ToS strike against your account - please see https://github.com/RogueException/Discord.Net/issues/827")]
[Obsolete("User logins are deprecated and may result in a ToS strike against your account - please see https://github.com/RogueException/Discord.Net/issues/827", error: true)]
if (args.Content.Value.Length > DiscordConfig.MaxMessageSize)
if (args.Content.Value?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
Thank you for your continuous support to the Openl Qizhi Community AI Collaboration Platform. In order to protect your usage rights and ensure network security, we updated the Openl Qizhi Community AI Collaboration Platform Usage Agreement in January 2024. The updated agreement specifies that users are prohibited from using intranet penetration tools. After you click "Agree and continue", you can continue to use our services. Thank you for your cooperation and understanding.