mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
several enhancements:
- fixed UpdateAffectedMessages on wrong mbox - MarkdownToEntities allow reserved characters in code block - collector system more flexible & open - improved UpdateManager resilience - some MTPG improvements
This commit is contained in:
parent
210a3365e5
commit
abeed476e7
2
FAQ.md
2
FAQ.md
|
|
@ -349,8 +349,6 @@ var manager = client.WithUpdateManager(OnUpdate);
|
||||||
var manager = client.WithUpdateManager(OnUpdate, "Updates.state");
|
var manager = client.WithUpdateManager(OnUpdate, "Updates.state");
|
||||||
// to save the state later, preferably after disposing the client:
|
// to save the state later, preferably after disposing the client:
|
||||||
manager.SaveState("Updates.state")
|
manager.SaveState("Updates.state")
|
||||||
|
|
||||||
// (WithUpdateManager has other parameters for advanced use)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Your `OnUpdate` method will directly take a single `Update` as parameter, instead of a container of updates.
|
Your `OnUpdate` method will directly take a single `Update` as parameter, instead of a container of updates.
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ See [Examples/Program_ListenUpdates.cs](https://github.com/wiz0u/WTelegramClient
|
||||||
An invalid API request can result in a `RpcException` being raised, reflecting the [error code and status text](https://revgram.github.io/errors.html) of the problem.
|
An invalid API request can result in a `RpcException` being raised, reflecting the [error code and status text](https://revgram.github.io/errors.html) of the problem.
|
||||||
|
|
||||||
To [prevent getting banned](https://wiz0u.github.io/WTelegramClient/FAQ#prevent-ban) during dev, you can connect to [test servers](https://docs.pyrogram.org/topics/test-servers), by adding this line in your Config callback:
|
To [prevent getting banned](https://wiz0u.github.io/WTelegramClient/FAQ#prevent-ban) during dev, you can connect to [test servers](https://docs.pyrogram.org/topics/test-servers), by adding this line in your Config callback:
|
||||||
`case "server_address": return "149.154.167.40:443"; // test DC`
|
`case "server_address": return "2>149.154.167.40:443"; // test DC`
|
||||||
|
|
||||||
The other configuration items that you can provide include: **session_pathname, email, email_verification_code, session_key, device_model, system_version, app_version, system_lang_code, lang_pack, lang_code, firebase, user_id, bot_token**
|
The other configuration items that you can provide include: **session_pathname, email, email_verification_code, session_key, device_model, system_version, app_version, system_lang_code, lang_pack, lang_code, firebase, user_id, bot_token**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,18 @@ public class MTProtoGenerator : IIncrementalGenerator
|
||||||
if (unit.compilation.GetTypeByMetadataName("TL.IObject") is not { } iobject) return;
|
if (unit.compilation.GetTypeByMetadataName("TL.IObject") is not { } iobject) return;
|
||||||
var nullables = LoadNullables(layer);
|
var nullables = LoadNullables(layer);
|
||||||
var namespaces = new Dictionary<string, Dictionary<string, string>>(); // namespace,class,methods
|
var namespaces = new Dictionary<string, Dictionary<string, string>>(); // namespace,class,methods
|
||||||
var readTL = new StringBuilder();
|
var makeTL = new StringBuilder();
|
||||||
readTL
|
var source = new StringBuilder();
|
||||||
|
source
|
||||||
|
.AppendLine("using System;")
|
||||||
|
.AppendLine("using System.ComponentModel;")
|
||||||
|
.AppendLine("using System.IO;")
|
||||||
|
.AppendLine("using System.Linq;")
|
||||||
|
.AppendLine("using TL;")
|
||||||
|
.AppendLine()
|
||||||
|
.AppendLine("#pragma warning disable CS0109")
|
||||||
|
.AppendLine();
|
||||||
|
makeTL
|
||||||
.AppendLine("\t\tpublic static IObject ReadTL(this BinaryReader reader, uint ctorId = 0) => (ctorId != 0 ? ctorId : reader.ReadUInt32()) switch")
|
.AppendLine("\t\tpublic static IObject ReadTL(this BinaryReader reader, uint ctorId = 0) => (ctorId != 0 ? ctorId : reader.ReadUInt32()) switch")
|
||||||
.AppendLine("\t\t{");
|
.AppendLine("\t\t{");
|
||||||
|
|
||||||
|
|
@ -55,6 +65,9 @@ public class MTProtoGenerator : IIncrementalGenerator
|
||||||
{
|
{
|
||||||
if (parentMethods == null)
|
if (parentMethods == null)
|
||||||
{
|
{
|
||||||
|
if (name is "Peer")
|
||||||
|
writeTl.AppendLine("\t\tpublic virtual void WriteTL(BinaryWriter writer) => throw new NotSupportedException();");
|
||||||
|
else
|
||||||
writeTl.AppendLine("\t\tpublic abstract void WriteTL(BinaryWriter writer);");
|
writeTl.AppendLine("\t\tpublic abstract void WriteTL(BinaryWriter writer);");
|
||||||
parentClasses[name] = writeTl.ToString();
|
parentClasses[name] = writeTl.ToString();
|
||||||
writeTl.Clear();
|
writeTl.Clear();
|
||||||
|
|
@ -72,17 +85,20 @@ public class MTProtoGenerator : IIncrementalGenerator
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (id == 0x3072CFA1) // GzipPacked
|
if (id == 0x3072CFA1) // GzipPacked
|
||||||
readTL.AppendLine($"\t\t\t0x{id:X8} => reader.ReadTLGzipped(),");
|
makeTL.AppendLine($"\t\t\t0x{id:X8} => reader.ReadTLGzipped(),");
|
||||||
else if (name != "Null" && (ns != "TL.Methods" || name == "Ping"))
|
else if (name != "Null" && (ns != "TL.Methods" || name == "Ping"))
|
||||||
readTL.AppendLine($"\t\t\t0x{id:X8} => new {(ns == "TL" ? "" : ns + '.')}{name}(reader),");
|
makeTL.AppendLine($"\t\t\t0x{id:X8} => new {(ns == "TL" ? "" : ns + '.')}{name}().ReadTL(reader),");
|
||||||
var override_ = symbol.BaseType == object_ ? "" : "override ";
|
var override_ = symbol.BaseType == object_ ? "" : "override ";
|
||||||
if (name == "Messages_AffectedMessages") override_ = "virtual ";
|
if (name == "Messages_AffectedMessages") override_ = "virtual ";
|
||||||
if (symbol.Constructors[0].IsImplicitlyDeclared)
|
//if (symbol.Constructors[0].IsImplicitlyDeclared)
|
||||||
ctorTL.AppendLine($"\t\tpublic {name}() {{ }}");
|
// ctorTL.AppendLine($"\t\tpublic {name}() {{ }}");
|
||||||
|
if (symbol.IsGenericType) name += "<X>";
|
||||||
ctorTL
|
ctorTL
|
||||||
.AppendLine($"\t\tpublic {name}(BinaryReader reader)")
|
.AppendLine("\t\t[EditorBrowsable(EditorBrowsableState.Never)]")
|
||||||
|
.AppendLine($"\t\tpublic new {name} ReadTL(BinaryReader reader)")
|
||||||
.AppendLine("\t\t{");
|
.AppendLine("\t\t{");
|
||||||
writeTl
|
writeTl
|
||||||
|
.AppendLine("\t\t[EditorBrowsable(EditorBrowsableState.Never)]")
|
||||||
.AppendLine($"\t\tpublic {override_}void WriteTL(BinaryWriter writer)")
|
.AppendLine($"\t\tpublic {override_}void WriteTL(BinaryWriter writer)")
|
||||||
.AppendLine("\t\t{")
|
.AppendLine("\t\t{")
|
||||||
.AppendLine($"\t\t\twriter.Write(0x{id:X8});");
|
.AppendLine($"\t\t\twriter.Write(0x{id:X8});");
|
||||||
|
|
@ -143,7 +159,7 @@ public class MTProtoGenerator : IIncrementalGenerator
|
||||||
writeTl.AppendLine($"writer.Write({member.Name});");
|
writeTl.AppendLine($"writer.Write({member.Name});");
|
||||||
break;
|
break;
|
||||||
case "TL._Message[]":
|
case "TL._Message[]":
|
||||||
ctorTL.AppendLine($"throw new NotSupportedException();");
|
ctorTL.AppendLine($"{member.Name} = reader.ReadTLRawVector<_Message>(0x5BB8E511);");
|
||||||
writeTl.AppendLine($"writer.WriteTLMessages({member.Name});");
|
writeTl.AppendLine($"writer.WriteTLMessages({member.Name});");
|
||||||
break;
|
break;
|
||||||
case "TL.IObject": case "TL.IMethod<X>":
|
case "TL.IObject": case "TL.IMethod<X>":
|
||||||
|
|
@ -183,25 +199,18 @@ public class MTProtoGenerator : IIncrementalGenerator
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctorTL.AppendLine("\t\t\treturn this;");
|
||||||
ctorTL.AppendLine("\t\t}");
|
ctorTL.AppendLine("\t\t}");
|
||||||
writeTl.AppendLine("\t\t}");
|
writeTl.AppendLine("\t\t}");
|
||||||
ctorTL.Append(writeTl.ToString());
|
ctorTL.Append(writeTl.ToString());
|
||||||
if (symbol.IsGenericType) name += "<X>";
|
|
||||||
classes[name] = ctorTL.ToString();
|
classes[name] = ctorTL.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = new StringBuilder();
|
|
||||||
source
|
|
||||||
.AppendLine("using System;")
|
|
||||||
.AppendLine("using System.IO;")
|
|
||||||
.AppendLine("using System.Linq;")
|
|
||||||
.AppendLine("using TL;")
|
|
||||||
.AppendLine();
|
|
||||||
foreach (var nullable in nullables)
|
foreach (var nullable in nullables)
|
||||||
readTL.AppendLine($"\t\t\t0x{nullable.Value:X8} => null,");
|
makeTL.AppendLine($"\t\t\t0x{nullable.Value:X8} => null,");
|
||||||
readTL.AppendLine("\t\t\tvar ctorNb => throw new Exception($\"Cannot find type for ctor #{ctorNb:x}\")");
|
makeTL.AppendLine("\t\t\tvar ctorNb => throw new Exception($\"Cannot find type for ctor #{ctorNb:x}\")");
|
||||||
readTL.AppendLine("\t\t};");
|
makeTL.AppendLine("\t\t};");
|
||||||
namespaces["TL"]["Layer"] = readTL.ToString();
|
namespaces["TL"]["Layer"] = makeTL.ToString();
|
||||||
foreach (var namesp in namespaces)
|
foreach (var namesp in namespaces)
|
||||||
{
|
{
|
||||||
source.Append("namespace ").AppendLine(namesp.Key).Append('{');
|
source.Append("namespace ").AppendLine(namesp.Key).Append('{');
|
||||||
|
|
|
||||||
|
|
@ -586,8 +586,6 @@ namespace WTelegram
|
||||||
if (OnOwnUpdates != null)
|
if (OnOwnUpdates != null)
|
||||||
if (result is UpdatesBase updates)
|
if (result is UpdatesBase updates)
|
||||||
RaiseOwnUpdates(updates);
|
RaiseOwnUpdates(updates);
|
||||||
else if (result is Messages_AffectedMessages affected)
|
|
||||||
RaiseOwnUpdates(new UpdateShort { update = new UpdateAffectedMessages { affected = affected }, date = MsgIdToStamp(_lastRecvMsgId) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rpc.tcs.SetResult(result);
|
rpc.tcs.SetResult(result);
|
||||||
|
|
@ -1454,5 +1452,16 @@ namespace WTelegram
|
||||||
throw new WTException($"{query.GetType().Name} call got a result of type {result.GetType().Name} instead of {typeof(T).Name}");
|
throw new WTException($"{query.GetType().Name} call got a result of type {result.GetType().Name} instead of {typeof(T).Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<T> InvokeAffected<T>(IMethod<T> query, long peerId) where T : Messages_AffectedMessages
|
||||||
|
{
|
||||||
|
var result = await Invoke(query);
|
||||||
|
RaiseOwnUpdates(new UpdateShort
|
||||||
|
{
|
||||||
|
update = new UpdateAffectedMessages { mbox_id = peerId, pts = result.pts, pts_count = result.pts_count},
|
||||||
|
date = MsgIdToStamp(_lastRecvMsgId)
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,9 @@ namespace WTelegram
|
||||||
|
|
||||||
public class WTException : ApplicationException
|
public class WTException : ApplicationException
|
||||||
{
|
{
|
||||||
|
public readonly int ErrorCode;
|
||||||
public WTException(string message) : base(message) { }
|
public WTException(string message) : base(message) { }
|
||||||
|
public WTException(string message, int code) : base(message) => ErrorCode = code;
|
||||||
public WTException(string message, Exception innerException) : base(message, innerException) { }
|
public WTException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,26 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using WTelegram; // for GetValueOrDefault
|
using WTelegram;
|
||||||
|
|
||||||
namespace TL
|
namespace TL
|
||||||
{
|
{
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
internal sealed partial class CollectorPeer : Peer
|
public sealed partial class CollectorPeer(IDictionary<long, User> _users, IDictionary<long, ChatBase> _chats) : Peer, IPeerCollector
|
||||||
{
|
{
|
||||||
public override long ID => 0;
|
public override long ID => 0;
|
||||||
internal IDictionary<long, User> _users;
|
|
||||||
internal IDictionary<long, ChatBase> _chats;
|
|
||||||
protected internal override IPeerInfo UserOrChat(Dictionary<long, User> users, Dictionary<long, ChatBase> chats)
|
protected internal override IPeerInfo UserOrChat(Dictionary<long, User> users, Dictionary<long, ChatBase> chats)
|
||||||
{
|
{
|
||||||
if (_users != null)
|
if (users != null) Collect(users.Values);
|
||||||
|
if (chats != null) Collect(chats.Values);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Collect(IEnumerable<TL.User> users)
|
||||||
|
{
|
||||||
lock (_users)
|
lock (_users)
|
||||||
foreach (var user in users.Values)
|
foreach (var user in users)
|
||||||
if (user != null)
|
if (user != null)
|
||||||
if (!user.flags.HasFlag(User.Flags.min) || !_users.TryGetValue(user.id, out var prevUser) || prevUser.flags.HasFlag(User.Flags.min))
|
if (!user.flags.HasFlag(User.Flags.min) || !_users.TryGetValue(user.id, out var prevUser) || prevUser.flags.HasFlag(User.Flags.min))
|
||||||
_users[user.id] = user;
|
_users[user.id] = user;
|
||||||
|
|
@ -56,13 +60,16 @@ namespace TL
|
||||||
prevUser.profile_color = user.profile_color; // tdlib/tdesktop: unimplemented yet
|
prevUser.profile_color = user.profile_color; // tdlib/tdesktop: unimplemented yet
|
||||||
_users[user.id] = prevUser;
|
_users[user.id] = prevUser;
|
||||||
}
|
}
|
||||||
if (_chats != null)
|
}
|
||||||
|
|
||||||
|
public void Collect(IEnumerable<ChatBase> chats)
|
||||||
|
{
|
||||||
lock (_chats)
|
lock (_chats)
|
||||||
foreach (var kvp in chats)
|
foreach (var chat in chats)
|
||||||
if (kvp.Value is not Channel channel)
|
if (chat is not Channel channel)
|
||||||
_chats[kvp.Key] = kvp.Value;
|
_chats[chat.ID] = chat;
|
||||||
else if (!channel.flags.HasFlag(Channel.Flags.min) || !_chats.TryGetValue(kvp.Key, out var prevChat) || prevChat is not Channel prevChannel || prevChannel.flags.HasFlag(Channel.Flags.min))
|
else if (!channel.flags.HasFlag(Channel.Flags.min) || !_chats.TryGetValue(channel.id, out var prevChat) || prevChat is not Channel prevChannel || prevChannel.flags.HasFlag(Channel.Flags.min))
|
||||||
_chats[kvp.Key] = channel;
|
_chats[channel.id] = channel;
|
||||||
else
|
else
|
||||||
{ // update previously full channel from min channel:
|
{ // update previously full channel from min channel:
|
||||||
const Channel.Flags updated_flags = (Channel.Flags)0x7FDC0BE0;
|
const Channel.Flags updated_flags = (Channel.Flags)0x7FDC0BE0;
|
||||||
|
|
@ -84,19 +91,18 @@ namespace TL
|
||||||
prevChannel.profile_color = channel.profile_color; // tdlib/tdesktop: ignored
|
prevChannel.profile_color = channel.profile_color; // tdlib/tdesktop: ignored
|
||||||
prevChannel.emoji_status = channel.emoji_status; // tdlib: not updated ; tdesktop: updated
|
prevChannel.emoji_status = channel.emoji_status; // tdlib: not updated ; tdesktop: updated
|
||||||
prevChannel.level = channel.level; // tdlib: not updated ; tdesktop: updated
|
prevChannel.level = channel.level; // tdlib: not updated ; tdesktop: updated
|
||||||
_chats[kvp.Key] = prevChannel;
|
_chats[channel.id] = prevChannel;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
#if MTPG
|
|
||||||
public override void WriteTL(System.IO.BinaryWriter writer) => throw new NotImplementedException();
|
public bool HasUser(long id) { lock (_users) return _users.ContainsKey(id); }
|
||||||
#endif
|
public bool HasChat(long id) { lock (_chats) return _chats.ContainsKey(id); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Accumulate users/chats found in this structure in your dictionaries, ignoring <see href="https://core.telegram.org/api/min">Min constructors</see> when the full object is already stored</summary>
|
/// <summary>Accumulate users/chats found in this structure in your dictionaries, ignoring <see href="https://core.telegram.org/api/min">Min constructors</see> when the full object is already stored</summary>
|
||||||
/// <param name="structure">The structure having a <c>users</c></param>
|
/// <param name="structure">The structure having a <c>users</c></param>
|
||||||
public static void CollectUsersChats(this IPeerResolver structure, IDictionary<long, User> users, IDictionary<long, ChatBase> chats)
|
public static void CollectUsersChats(this IPeerResolver structure, IDictionary<long, User> users, IDictionary<long, ChatBase> chats)
|
||||||
=> structure.UserOrChat(new CollectorPeer { _users = users, _chats = chats });
|
=> structure.UserOrChat(new CollectorPeer(users, chats));
|
||||||
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
public static Task<Messages_Chats> Messages_GetChats(this Client _) => throw new WTException("The method you're looking for is Messages_GetAllChats");
|
public static Task<Messages_Chats> Messages_GetChats(this Client _) => throw new WTException("The method you're looking for is Messages_GetAllChats");
|
||||||
|
|
@ -120,6 +126,7 @@ namespace TL
|
||||||
{
|
{
|
||||||
var entities = new List<MessageEntity>();
|
var entities = new List<MessageEntity>();
|
||||||
MessageEntityBlockquote lastBlockQuote = null;
|
MessageEntityBlockquote lastBlockQuote = null;
|
||||||
|
int inCode = 0;
|
||||||
var sb = new StringBuilder(text);
|
var sb = new StringBuilder(text);
|
||||||
for (int offset = 0; offset < sb.Length;)
|
for (int offset = 0; offset < sb.Length;)
|
||||||
{
|
{
|
||||||
|
|
@ -127,9 +134,9 @@ namespace TL
|
||||||
{
|
{
|
||||||
case '\r': sb.Remove(offset, 1); break;
|
case '\r': sb.Remove(offset, 1); break;
|
||||||
case '\\': sb.Remove(offset++, 1); break;
|
case '\\': sb.Remove(offset++, 1); break;
|
||||||
case '*': ProcessEntity<MessageEntityBold>(); break;
|
case '*' when inCode == 0: ProcessEntity<MessageEntityBold>(); break;
|
||||||
case '~': ProcessEntity<MessageEntityStrike>(); break;
|
case '~' when inCode == 0: ProcessEntity<MessageEntityStrike>(); break;
|
||||||
case '_':
|
case '_' when inCode == 0:
|
||||||
if (offset + 1 < sb.Length && sb[offset + 1] == '_')
|
if (offset + 1 < sb.Length && sb[offset + 1] == '_')
|
||||||
{
|
{
|
||||||
sb.Remove(offset, 1);
|
sb.Remove(offset, 1);
|
||||||
|
|
@ -139,7 +146,7 @@ namespace TL
|
||||||
ProcessEntity<MessageEntityItalic>();
|
ProcessEntity<MessageEntityItalic>();
|
||||||
break;
|
break;
|
||||||
case '|':
|
case '|':
|
||||||
if (offset + 1 < sb.Length && sb[offset + 1] == '|')
|
if (inCode == 0 && offset + 1 < sb.Length && sb[offset + 1] == '|')
|
||||||
{
|
{
|
||||||
sb.Remove(offset, 1);
|
sb.Remove(offset, 1);
|
||||||
ProcessEntity<MessageEntitySpoiler>();
|
ProcessEntity<MessageEntitySpoiler>();
|
||||||
|
|
@ -148,6 +155,7 @@ namespace TL
|
||||||
offset++;
|
offset++;
|
||||||
break;
|
break;
|
||||||
case '`':
|
case '`':
|
||||||
|
int count = entities.Count;
|
||||||
if (offset + 2 < sb.Length && sb[offset + 1] == '`' && sb[offset + 2] == '`')
|
if (offset + 2 < sb.Length && sb[offset + 1] == '`' && sb[offset + 2] == '`')
|
||||||
{
|
{
|
||||||
int len = 3;
|
int len = 3;
|
||||||
|
|
@ -164,8 +172,9 @@ namespace TL
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ProcessEntity<MessageEntityCode>();
|
ProcessEntity<MessageEntityCode>();
|
||||||
|
if (entities.Count > count) inCode++; else inCode--;
|
||||||
break;
|
break;
|
||||||
case '>' when offset == 0 || sb[offset - 1] == '\n':
|
case '>' when inCode == 0 && offset == 0 || sb[offset - 1] == '\n':
|
||||||
sb.Remove(offset, 1);
|
sb.Remove(offset, 1);
|
||||||
if (lastBlockQuote is null || lastBlockQuote.length < offset - lastBlockQuote.offset)
|
if (lastBlockQuote is null || lastBlockQuote.length < offset - lastBlockQuote.offset)
|
||||||
entities.Add(lastBlockQuote = new MessageEntityBlockquote { offset = offset, length = -1 });
|
entities.Add(lastBlockQuote = new MessageEntityBlockquote { offset = offset, length = -1 });
|
||||||
|
|
@ -175,15 +184,15 @@ namespace TL
|
||||||
case '\n' when lastBlockQuote is { length: -1 }:
|
case '\n' when lastBlockQuote is { length: -1 }:
|
||||||
lastBlockQuote.length = ++offset - lastBlockQuote.offset;
|
lastBlockQuote.length = ++offset - lastBlockQuote.offset;
|
||||||
break;
|
break;
|
||||||
case '!' when offset + 1 < sb.Length && sb[offset + 1] == '[':
|
case '!' when inCode == 0 && offset + 1 < sb.Length && sb[offset + 1] == '[':
|
||||||
sb.Remove(offset, 1);
|
sb.Remove(offset, 1);
|
||||||
goto case '[';
|
break;
|
||||||
case '[':
|
case '[' when inCode == 0:
|
||||||
entities.Add(new MessageEntityTextUrl { offset = offset, length = -1 });
|
entities.Add(new MessageEntityTextUrl { offset = offset, length = -1 });
|
||||||
sb.Remove(offset, 1);
|
sb.Remove(offset, 1);
|
||||||
break;
|
break;
|
||||||
case ']':
|
case ']':
|
||||||
if (offset + 2 < sb.Length && sb[offset + 1] == '(')
|
if (inCode == 0 && offset + 2 < sb.Length && sb[offset + 1] == '(')
|
||||||
{
|
{
|
||||||
var lastIndex = entities.FindLastIndex(e => e.length == -1);
|
var lastIndex = entities.FindLastIndex(e => e.length == -1);
|
||||||
if (lastIndex >= 0 && entities[lastIndex] is MessageEntityTextUrl textUrl)
|
if (lastIndex >= 0 && entities[lastIndex] is MessageEntityTextUrl textUrl)
|
||||||
|
|
@ -213,11 +222,14 @@ namespace TL
|
||||||
|
|
||||||
void ProcessEntity<T>() where T : MessageEntity, new()
|
void ProcessEntity<T>() where T : MessageEntity, new()
|
||||||
{
|
{
|
||||||
|
sb.Remove(offset, 1);
|
||||||
if (entities.LastOrDefault(e => e.length == -1) is T prevEntity)
|
if (entities.LastOrDefault(e => e.length == -1) is T prevEntity)
|
||||||
|
if (offset == prevEntity.offset)
|
||||||
|
entities.Remove(prevEntity);
|
||||||
|
else
|
||||||
prevEntity.length = offset - prevEntity.offset;
|
prevEntity.length = offset - prevEntity.offset;
|
||||||
else
|
else
|
||||||
entities.Add(new T { offset = offset, length = -1 });
|
entities.Add(new T { offset = offset, length = -1 });
|
||||||
sb.Remove(offset, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lastBlockQuote is { length: -1 })
|
if (lastBlockQuote is { length: -1 })
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ namespace TL
|
||||||
{
|
{
|
||||||
file = inputFile;
|
file = inputFile;
|
||||||
mime_type = mimeType;
|
mime_type = mimeType;
|
||||||
if (inputFile.Name is string filename) attributes = new[] { new DocumentAttributeFilename { file_name = filename } };
|
if (inputFile.Name is string filename) attributes = [new DocumentAttributeFilename { file_name = filename }];
|
||||||
}
|
}
|
||||||
public InputMediaUploadedDocument(InputFileBase inputFile, string mimeType, params DocumentAttribute[] attribs)
|
public InputMediaUploadedDocument(InputFileBase inputFile, string mimeType, params DocumentAttribute[] attribs)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1604,11 +1604,11 @@ namespace TL
|
||||||
/// <param name="peer">Target user or group</param>
|
/// <param name="peer">Target user or group</param>
|
||||||
/// <param name="max_id">If a positive value is passed, only messages with identifiers less or equal than the given one will be read</param>
|
/// <param name="max_id">If a positive value is passed, only messages with identifiers less or equal than the given one will be read</param>
|
||||||
public static Task<Messages_AffectedMessages> Messages_ReadHistory(this Client client, InputPeer peer, int max_id = default)
|
public static Task<Messages_AffectedMessages> Messages_ReadHistory(this Client client, InputPeer peer, int max_id = default)
|
||||||
=> client.Invoke(new Messages_ReadHistory
|
=> client.InvokeAffected(new Messages_ReadHistory
|
||||||
{
|
{
|
||||||
peer = peer,
|
peer = peer,
|
||||||
max_id = max_id,
|
max_id = max_id,
|
||||||
});
|
}, peer is InputPeerChannel ipc ? ipc.channel_id : 0);
|
||||||
|
|
||||||
/// <summary>Deletes communication history. <para>See <a href="https://corefork.telegram.org/method/messages.deleteHistory"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.deleteHistory#possible-errors">details</a>)</para></summary>
|
/// <summary>Deletes communication history. <para>See <a href="https://corefork.telegram.org/method/messages.deleteHistory"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.deleteHistory#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="just_clear">Just clear history for the current user, without actually removing messages for every chat user</param>
|
/// <param name="just_clear">Just clear history for the current user, without actually removing messages for every chat user</param>
|
||||||
|
|
@ -1618,24 +1618,24 @@ namespace TL
|
||||||
/// <param name="min_date">Delete all messages newer than this UNIX timestamp</param>
|
/// <param name="min_date">Delete all messages newer than this UNIX timestamp</param>
|
||||||
/// <param name="max_date">Delete all messages older than this UNIX timestamp</param>
|
/// <param name="max_date">Delete all messages older than this UNIX timestamp</param>
|
||||||
public static Task<Messages_AffectedHistory> Messages_DeleteHistory(this Client client, InputPeer peer, int max_id = default, DateTime? min_date = null, DateTime? max_date = null, bool just_clear = false, bool revoke = false)
|
public static Task<Messages_AffectedHistory> Messages_DeleteHistory(this Client client, InputPeer peer, int max_id = default, DateTime? min_date = null, DateTime? max_date = null, bool just_clear = false, bool revoke = false)
|
||||||
=> client.Invoke(new Messages_DeleteHistory
|
=> client.InvokeAffected(new Messages_DeleteHistory
|
||||||
{
|
{
|
||||||
flags = (Messages_DeleteHistory.Flags)((min_date != null ? 0x4 : 0) | (max_date != null ? 0x8 : 0) | (just_clear ? 0x1 : 0) | (revoke ? 0x2 : 0)),
|
flags = (Messages_DeleteHistory.Flags)((min_date != null ? 0x4 : 0) | (max_date != null ? 0x8 : 0) | (just_clear ? 0x1 : 0) | (revoke ? 0x2 : 0)),
|
||||||
peer = peer,
|
peer = peer,
|
||||||
max_id = max_id,
|
max_id = max_id,
|
||||||
min_date = min_date.GetValueOrDefault(),
|
min_date = min_date.GetValueOrDefault(),
|
||||||
max_date = max_date.GetValueOrDefault(),
|
max_date = max_date.GetValueOrDefault(),
|
||||||
});
|
}, peer is InputPeerChannel ipc ? ipc.channel_id : 0);
|
||||||
|
|
||||||
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Deletes messages by their identifiers. <para>See <a href="https://corefork.telegram.org/method/messages.deleteMessages"/> [bots: ✓]</para> <para>Possible <see cref="RpcException"/> codes: 400,403 (<a href="https://corefork.telegram.org/method/messages.deleteMessages#possible-errors">details</a>)</para></summary>
|
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Deletes messages by their identifiers. <para>See <a href="https://corefork.telegram.org/method/messages.deleteMessages"/> [bots: ✓]</para> <para>Possible <see cref="RpcException"/> codes: 400,403 (<a href="https://corefork.telegram.org/method/messages.deleteMessages#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="revoke">Whether to delete messages for all participants of the chat</param>
|
/// <param name="revoke">Whether to delete messages for all participants of the chat</param>
|
||||||
/// <param name="id">Message ID list</param>
|
/// <param name="id">Message ID list</param>
|
||||||
public static Task<Messages_AffectedMessages> Messages_DeleteMessages(this Client client, int[] id, bool revoke = false)
|
public static Task<Messages_AffectedMessages> Messages_DeleteMessages(this Client client, int[] id, bool revoke = false)
|
||||||
=> client.Invoke(new Messages_DeleteMessages
|
=> client.InvokeAffected(new Messages_DeleteMessages
|
||||||
{
|
{
|
||||||
flags = (Messages_DeleteMessages.Flags)(revoke ? 0x1 : 0),
|
flags = (Messages_DeleteMessages.Flags)(revoke ? 0x1 : 0),
|
||||||
id = id,
|
id = id,
|
||||||
});
|
}, 0);
|
||||||
|
|
||||||
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Confirms receipt of messages by a client, cancels PUSH-notification sending. <para>See <a href="https://corefork.telegram.org/method/messages.receivedMessages"/></para></summary>
|
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Confirms receipt of messages by a client, cancels PUSH-notification sending. <para>See <a href="https://corefork.telegram.org/method/messages.receivedMessages"/></para></summary>
|
||||||
/// <param name="max_id">Maximum message ID available in a client.</param>
|
/// <param name="max_id">Maximum message ID available in a client.</param>
|
||||||
|
|
@ -1977,10 +1977,10 @@ namespace TL
|
||||||
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Notifies the sender about the recipient having listened a voice message or watched a video. <para>See <a href="https://corefork.telegram.org/method/messages.readMessageContents"/></para></summary>
|
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Notifies the sender about the recipient having listened a voice message or watched a video. <para>See <a href="https://corefork.telegram.org/method/messages.readMessageContents"/></para></summary>
|
||||||
/// <param name="id">Message ID list</param>
|
/// <param name="id">Message ID list</param>
|
||||||
public static Task<Messages_AffectedMessages> Messages_ReadMessageContents(this Client client, params int[] id)
|
public static Task<Messages_AffectedMessages> Messages_ReadMessageContents(this Client client, params int[] id)
|
||||||
=> client.Invoke(new Messages_ReadMessageContents
|
=> client.InvokeAffected(new Messages_ReadMessageContents
|
||||||
{
|
{
|
||||||
id = id,
|
id = id,
|
||||||
});
|
}, 0);
|
||||||
|
|
||||||
/// <summary>Get stickers by emoji <para>See <a href="https://corefork.telegram.org/method/messages.getStickers"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.getStickers#possible-errors">details</a>)</para></summary>
|
/// <summary>Get stickers by emoji <para>See <a href="https://corefork.telegram.org/method/messages.getStickers"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.getStickers#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="emoticon">The emoji</param>
|
/// <param name="emoticon">The emoji</param>
|
||||||
|
|
@ -2646,12 +2646,12 @@ namespace TL
|
||||||
/// <param name="peer">Dialog</param>
|
/// <param name="peer">Dialog</param>
|
||||||
/// <param name="top_msg_id">Mark as read only mentions within the specified <a href="https://corefork.telegram.org/api/forum#forum-topics">forum topic</a></param>
|
/// <param name="top_msg_id">Mark as read only mentions within the specified <a href="https://corefork.telegram.org/api/forum#forum-topics">forum topic</a></param>
|
||||||
public static Task<Messages_AffectedHistory> Messages_ReadMentions(this Client client, InputPeer peer, int? top_msg_id = null)
|
public static Task<Messages_AffectedHistory> Messages_ReadMentions(this Client client, InputPeer peer, int? top_msg_id = null)
|
||||||
=> client.Invoke(new Messages_ReadMentions
|
=> client.InvokeAffected(new Messages_ReadMentions
|
||||||
{
|
{
|
||||||
flags = (Messages_ReadMentions.Flags)(top_msg_id != null ? 0x1 : 0),
|
flags = (Messages_ReadMentions.Flags)(top_msg_id != null ? 0x1 : 0),
|
||||||
peer = peer,
|
peer = peer,
|
||||||
top_msg_id = top_msg_id.GetValueOrDefault(),
|
top_msg_id = top_msg_id.GetValueOrDefault(),
|
||||||
});
|
}, peer is InputPeerChannel ipc ? ipc.channel_id : 0);
|
||||||
|
|
||||||
/// <summary>Get live location history of a certain user <para>See <a href="https://corefork.telegram.org/method/messages.getRecentLocations"/></para></summary>
|
/// <summary>Get live location history of a certain user <para>See <a href="https://corefork.telegram.org/method/messages.getRecentLocations"/></para></summary>
|
||||||
/// <param name="peer">User</param>
|
/// <param name="peer">User</param>
|
||||||
|
|
@ -3057,12 +3057,12 @@ namespace TL
|
||||||
/// <param name="peer">Chat where to unpin</param>
|
/// <param name="peer">Chat where to unpin</param>
|
||||||
/// <param name="top_msg_id"><a href="https://corefork.telegram.org/api/forum#forum-topics">Forum topic</a> where to unpin</param>
|
/// <param name="top_msg_id"><a href="https://corefork.telegram.org/api/forum#forum-topics">Forum topic</a> where to unpin</param>
|
||||||
public static Task<Messages_AffectedHistory> Messages_UnpinAllMessages(this Client client, InputPeer peer, int? top_msg_id = null)
|
public static Task<Messages_AffectedHistory> Messages_UnpinAllMessages(this Client client, InputPeer peer, int? top_msg_id = null)
|
||||||
=> client.Invoke(new Messages_UnpinAllMessages
|
=> client.InvokeAffected(new Messages_UnpinAllMessages
|
||||||
{
|
{
|
||||||
flags = (Messages_UnpinAllMessages.Flags)(top_msg_id != null ? 0x1 : 0),
|
flags = (Messages_UnpinAllMessages.Flags)(top_msg_id != null ? 0x1 : 0),
|
||||||
peer = peer,
|
peer = peer,
|
||||||
top_msg_id = top_msg_id.GetValueOrDefault(),
|
top_msg_id = top_msg_id.GetValueOrDefault(),
|
||||||
});
|
}, peer is InputPeerChannel ipc ? ipc.channel_id : 0);
|
||||||
|
|
||||||
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Delete a <a href="https://corefork.telegram.org/api/channel">chat</a> <para>See <a href="https://corefork.telegram.org/method/messages.deleteChat"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.deleteChat#possible-errors">details</a>)</para></summary>
|
/// <summary><para>⚠ <b>This method is only for basic Chat</b>. See <see href="https://wiz0u.github.io/WTelegramClient/#terminology">Terminology</see> in the README to understand what this means<br/>Search for a similar method name starting with <c>Channels_</c> if you're dealing with a <see cref="Channel"/></para> Delete a <a href="https://corefork.telegram.org/api/channel">chat</a> <para>See <a href="https://corefork.telegram.org/method/messages.deleteChat"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.deleteChat#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="chat_id">Chat ID</param>
|
/// <param name="chat_id">Chat ID</param>
|
||||||
|
|
@ -3075,10 +3075,10 @@ namespace TL
|
||||||
/// <summary>Delete the entire phone call history. <para>See <a href="https://corefork.telegram.org/method/messages.deletePhoneCallHistory"/></para></summary>
|
/// <summary>Delete the entire phone call history. <para>See <a href="https://corefork.telegram.org/method/messages.deletePhoneCallHistory"/></para></summary>
|
||||||
/// <param name="revoke">Whether to remove phone call history for participants as well</param>
|
/// <param name="revoke">Whether to remove phone call history for participants as well</param>
|
||||||
public static Task<Messages_AffectedFoundMessages> Messages_DeletePhoneCallHistory(this Client client, bool revoke = false)
|
public static Task<Messages_AffectedFoundMessages> Messages_DeletePhoneCallHistory(this Client client, bool revoke = false)
|
||||||
=> client.Invoke(new Messages_DeletePhoneCallHistory
|
=> client.InvokeAffected(new Messages_DeletePhoneCallHistory
|
||||||
{
|
{
|
||||||
flags = (Messages_DeletePhoneCallHistory.Flags)(revoke ? 0x1 : 0),
|
flags = (Messages_DeletePhoneCallHistory.Flags)(revoke ? 0x1 : 0),
|
||||||
});
|
}, 0);
|
||||||
|
|
||||||
/// <summary>Obtains information about a chat export file, generated by a foreign chat app, <a href="https://corefork.telegram.org/api/import">click here for more info about imported chats »</a>. <para>See <a href="https://corefork.telegram.org/method/messages.checkHistoryImport"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.checkHistoryImport#possible-errors">details</a>)</para></summary>
|
/// <summary>Obtains information about a chat export file, generated by a foreign chat app, <a href="https://corefork.telegram.org/api/import">click here for more info about imported chats »</a>. <para>See <a href="https://corefork.telegram.org/method/messages.checkHistoryImport"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.checkHistoryImport#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="import_head">Beginning of the message file; up to 100 lines.</param>
|
/// <param name="import_head">Beginning of the message file; up to 100 lines.</param>
|
||||||
|
|
@ -3446,12 +3446,12 @@ namespace TL
|
||||||
/// <param name="peer">Peer</param>
|
/// <param name="peer">Peer</param>
|
||||||
/// <param name="top_msg_id">Mark as read only reactions to messages within the specified <a href="https://corefork.telegram.org/api/forum#forum-topics">forum topic</a></param>
|
/// <param name="top_msg_id">Mark as read only reactions to messages within the specified <a href="https://corefork.telegram.org/api/forum#forum-topics">forum topic</a></param>
|
||||||
public static Task<Messages_AffectedHistory> Messages_ReadReactions(this Client client, InputPeer peer, int? top_msg_id = null)
|
public static Task<Messages_AffectedHistory> Messages_ReadReactions(this Client client, InputPeer peer, int? top_msg_id = null)
|
||||||
=> client.Invoke(new Messages_ReadReactions
|
=> client.InvokeAffected(new Messages_ReadReactions
|
||||||
{
|
{
|
||||||
flags = (Messages_ReadReactions.Flags)(top_msg_id != null ? 0x1 : 0),
|
flags = (Messages_ReadReactions.Flags)(top_msg_id != null ? 0x1 : 0),
|
||||||
peer = peer,
|
peer = peer,
|
||||||
top_msg_id = top_msg_id.GetValueOrDefault(),
|
top_msg_id = top_msg_id.GetValueOrDefault(),
|
||||||
});
|
}, peer is InputPeerChannel ipc ? ipc.channel_id : 0);
|
||||||
|
|
||||||
/// <summary>View and search recently sent media.<br/>This method does not support pagination. <para>See <a href="https://corefork.telegram.org/method/messages.searchSentMedia"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.searchSentMedia#possible-errors">details</a>)</para></summary>
|
/// <summary>View and search recently sent media.<br/>This method does not support pagination. <para>See <a href="https://corefork.telegram.org/method/messages.searchSentMedia"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/messages.searchSentMedia#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="q">Optional search query</param>
|
/// <param name="q">Optional search query</param>
|
||||||
|
|
@ -3859,14 +3859,14 @@ namespace TL
|
||||||
/// <param name="min_date">Delete all messages newer than this UNIX timestamp</param>
|
/// <param name="min_date">Delete all messages newer than this UNIX timestamp</param>
|
||||||
/// <param name="max_date">Delete all messages older than this UNIX timestamp</param>
|
/// <param name="max_date">Delete all messages older than this UNIX timestamp</param>
|
||||||
public static Task<Messages_AffectedHistory> Messages_DeleteSavedHistory(this Client client, InputPeer peer, int max_id = default, DateTime? min_date = null, DateTime? max_date = null)
|
public static Task<Messages_AffectedHistory> Messages_DeleteSavedHistory(this Client client, InputPeer peer, int max_id = default, DateTime? min_date = null, DateTime? max_date = null)
|
||||||
=> client.Invoke(new Messages_DeleteSavedHistory
|
=> client.InvokeAffected(new Messages_DeleteSavedHistory
|
||||||
{
|
{
|
||||||
flags = (Messages_DeleteSavedHistory.Flags)((min_date != null ? 0x4 : 0) | (max_date != null ? 0x8 : 0)),
|
flags = (Messages_DeleteSavedHistory.Flags)((min_date != null ? 0x4 : 0) | (max_date != null ? 0x8 : 0)),
|
||||||
peer = peer,
|
peer = peer,
|
||||||
max_id = max_id,
|
max_id = max_id,
|
||||||
min_date = min_date.GetValueOrDefault(),
|
min_date = min_date.GetValueOrDefault(),
|
||||||
max_date = max_date.GetValueOrDefault(),
|
max_date = max_date.GetValueOrDefault(),
|
||||||
});
|
}, peer is InputPeerChannel ipc ? ipc.channel_id : 0);
|
||||||
|
|
||||||
/// <summary>Get pinned <a href="https://corefork.telegram.org/api/saved-messages">saved dialogs, see here »</a> for more info. <para>See <a href="https://corefork.telegram.org/method/messages.getPinnedSavedDialogs"/></para></summary>
|
/// <summary>Get pinned <a href="https://corefork.telegram.org/api/saved-messages">saved dialogs, see here »</a> for more info. <para>See <a href="https://corefork.telegram.org/method/messages.getPinnedSavedDialogs"/></para></summary>
|
||||||
public static Task<Messages_SavedDialogsBase> Messages_GetPinnedSavedDialogs(this Client client)
|
public static Task<Messages_SavedDialogsBase> Messages_GetPinnedSavedDialogs(this Client client)
|
||||||
|
|
@ -4421,11 +4421,11 @@ namespace TL
|
||||||
/// <param name="channel"><a href="https://corefork.telegram.org/api/channel">Channel/supergroup</a></param>
|
/// <param name="channel"><a href="https://corefork.telegram.org/api/channel">Channel/supergroup</a></param>
|
||||||
/// <param name="id">IDs of messages to delete</param>
|
/// <param name="id">IDs of messages to delete</param>
|
||||||
public static Task<Messages_AffectedMessages> Channels_DeleteMessages(this Client client, InputChannelBase channel, params int[] id)
|
public static Task<Messages_AffectedMessages> Channels_DeleteMessages(this Client client, InputChannelBase channel, params int[] id)
|
||||||
=> client.Invoke(new Channels_DeleteMessages
|
=> client.InvokeAffected(new Channels_DeleteMessages
|
||||||
{
|
{
|
||||||
channel = channel,
|
channel = channel,
|
||||||
id = id,
|
id = id,
|
||||||
});
|
}, channel.ChannelId);
|
||||||
|
|
||||||
/// <summary>Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup <para>See <a href="https://corefork.telegram.org/method/channels.reportSpam"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/channels.reportSpam#possible-errors">details</a>)</para></summary>
|
/// <summary>Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup <para>See <a href="https://corefork.telegram.org/method/channels.reportSpam"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/channels.reportSpam#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="channel">Supergroup</param>
|
/// <param name="channel">Supergroup</param>
|
||||||
|
|
@ -4811,11 +4811,11 @@ namespace TL
|
||||||
/// <param name="channel">Supergroup</param>
|
/// <param name="channel">Supergroup</param>
|
||||||
/// <param name="participant">The participant whose messages should be deleted</param>
|
/// <param name="participant">The participant whose messages should be deleted</param>
|
||||||
public static Task<Messages_AffectedHistory> Channels_DeleteParticipantHistory(this Client client, InputChannelBase channel, InputPeer participant)
|
public static Task<Messages_AffectedHistory> Channels_DeleteParticipantHistory(this Client client, InputChannelBase channel, InputPeer participant)
|
||||||
=> client.Invoke(new Channels_DeleteParticipantHistory
|
=> client.InvokeAffected(new Channels_DeleteParticipantHistory
|
||||||
{
|
{
|
||||||
channel = channel,
|
channel = channel,
|
||||||
participant = participant,
|
participant = participant,
|
||||||
});
|
}, channel.ChannelId);
|
||||||
|
|
||||||
/// <summary>Set whether all users <a href="https://corefork.telegram.org/api/discussion#requiring-users-to-join-the-group">should join a discussion group in order to comment on a post »</a> <para>See <a href="https://corefork.telegram.org/method/channels.toggleJoinToSend"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/channels.toggleJoinToSend#possible-errors">details</a>)</para></summary>
|
/// <summary>Set whether all users <a href="https://corefork.telegram.org/api/discussion#requiring-users-to-join-the-group">should join a discussion group in order to comment on a post »</a> <para>See <a href="https://corefork.telegram.org/method/channels.toggleJoinToSend"/></para> <para>Possible <see cref="RpcException"/> codes: 400 (<a href="https://corefork.telegram.org/method/channels.toggleJoinToSend#possible-errors">details</a>)</para></summary>
|
||||||
/// <param name="channel">Discussion group</param>
|
/// <param name="channel">Discussion group</param>
|
||||||
|
|
@ -4960,11 +4960,11 @@ namespace TL
|
||||||
/// <param name="channel">Forum</param>
|
/// <param name="channel">Forum</param>
|
||||||
/// <param name="top_msg_id">Topic ID</param>
|
/// <param name="top_msg_id">Topic ID</param>
|
||||||
public static Task<Messages_AffectedHistory> Channels_DeleteTopicHistory(this Client client, InputChannelBase channel, int top_msg_id)
|
public static Task<Messages_AffectedHistory> Channels_DeleteTopicHistory(this Client client, InputChannelBase channel, int top_msg_id)
|
||||||
=> client.Invoke(new Channels_DeleteTopicHistory
|
=> client.InvokeAffected(new Channels_DeleteTopicHistory
|
||||||
{
|
{
|
||||||
channel = channel,
|
channel = channel,
|
||||||
top_msg_id = top_msg_id,
|
top_msg_id = top_msg_id,
|
||||||
});
|
}, channel.ChannelId);
|
||||||
|
|
||||||
/// <summary>Reorder pinned forum topics <para>See <a href="https://corefork.telegram.org/method/channels.reorderPinnedForumTopics"/> [bots: ✓]</para></summary>
|
/// <summary>Reorder pinned forum topics <para>See <a href="https://corefork.telegram.org/method/channels.reorderPinnedForumTopics"/> [bots: ✓]</para></summary>
|
||||||
/// <param name="force">If not set, the order of only the topics present both server-side and in <c>order</c> will be changed (i.e. mentioning topics not pinned server-side in <c>order</c> will not pin them, and not mentioning topics pinned server-side will not unpin them). <br/>If set, the entire server-side pinned topic list will be replaced with <c>order</c> (i.e. mentioning topics not pinned server-side in <c>order</c> will pin them, and not mentioning topics pinned server-side will unpin them)</param>
|
/// <param name="force">If not set, the order of only the topics present both server-side and in <c>order</c> will be changed (i.e. mentioning topics not pinned server-side in <c>order</c> will not pin them, and not mentioning topics pinned server-side will not unpin them). <br/>If set, the entire server-side pinned topic list will be replaced with <c>order</c> (i.e. mentioning topics not pinned server-side in <c>order</c> will pin them, and not mentioning topics pinned server-side will unpin them)</param>
|
||||||
|
|
|
||||||
10
src/TL.cs
10
src/TL.cs
|
|
@ -32,9 +32,9 @@ namespace TL
|
||||||
public readonly int Bit = bit;
|
public readonly int Bit = bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class RpcException(int code, string message, int x = -1) : WTelegram.WTException(message)
|
public sealed class RpcException(int code, string message, int x = -1) : WTelegram.WTException(message, code)
|
||||||
{
|
{
|
||||||
public readonly int Code = code;
|
public int Code => ErrorCode;
|
||||||
/// <summary>The value of X in the message, -1 if no variable X was found</summary>
|
/// <summary>The value of X in the message, -1 if no variable X was found</summary>
|
||||||
public readonly int X = x;
|
public readonly int X = x;
|
||||||
public override string ToString() { var str = base.ToString(); return str.Insert(str.IndexOf(':') + 1, " " + Code); }
|
public override string ToString() { var str = base.ToString(); return str.Insert(str.IndexOf(':') + 1, " " + Code); }
|
||||||
|
|
@ -398,8 +398,10 @@ namespace TL
|
||||||
|
|
||||||
public sealed partial class UpdateAffectedMessages : Update // auto-generated for OnOwnUpdates in case of such API call result
|
public sealed partial class UpdateAffectedMessages : Update // auto-generated for OnOwnUpdates in case of such API call result
|
||||||
{
|
{
|
||||||
public Messages_AffectedMessages affected;
|
public long mbox_id;
|
||||||
public override (long, int, int) GetMBox() => (0, affected.pts, affected.pts_count);
|
public int pts;
|
||||||
|
public int pts_count;
|
||||||
|
public override (long, int, int) GetMBox() => (mbox_id, pts, pts_count);
|
||||||
#if MTPG
|
#if MTPG
|
||||||
public override void WriteTL(BinaryWriter writer) => throw new NotSupportedException();
|
public override void WriteTL(BinaryWriter writer) => throw new NotSupportedException();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ using TL;
|
||||||
|
|
||||||
namespace WTelegram
|
namespace WTelegram
|
||||||
{
|
{
|
||||||
public delegate IPeerInfo UserChatCollector(Dictionary<long, User> users, Dictionary<long, ChatBase> chats);
|
|
||||||
|
|
||||||
public class UpdateManager : IPeerResolver
|
public class UpdateManager : IPeerResolver
|
||||||
{
|
{
|
||||||
/// <summary>Collected info about Users <i>(only if using the default collector)</i></summary>
|
/// <summary>Collected info about Users <i>(only if using the default collector)</i></summary>
|
||||||
|
|
@ -37,10 +35,9 @@ namespace WTelegram
|
||||||
|
|
||||||
private readonly Client _client;
|
private readonly Client _client;
|
||||||
private readonly Func<Update, Task> _onUpdate;
|
private readonly Func<Update, Task> _onUpdate;
|
||||||
private readonly UserChatCollector _onCollect;
|
private readonly IPeerCollector _collector;
|
||||||
private readonly bool _reentrant;
|
private readonly bool _reentrant;
|
||||||
private readonly SemaphoreSlim _sem = new(1);
|
private readonly SemaphoreSlim _sem = new(1);
|
||||||
private readonly Extensions.CollectorPeer _collector;
|
|
||||||
private readonly List<(Update update, UpdatesBase updates, bool own, DateTime stamp)> _pending = [];
|
private readonly List<(Update update, UpdatesBase updates, bool own, DateTime stamp)> _pending = [];
|
||||||
private readonly Dictionary<long, MBoxState> _local; // -2 for seq/date, -1 for qts, 0 for common pts, >0 for channel pts
|
private readonly Dictionary<long, MBoxState> _local; // -2 for seq/date, -1 for qts, 0 for common pts, >0 for channel pts
|
||||||
private const int L_SEQ = -2, L_QTS = -1, L_PTS = 0;
|
private const int L_SEQ = -2, L_QTS = -1, L_PTS = 0;
|
||||||
|
|
@ -55,17 +52,11 @@ namespace WTelegram
|
||||||
/// <param name="state">(optional) Resume session by recovering all updates that occured since this state</param>
|
/// <param name="state">(optional) Resume session by recovering all updates that occured since this state</param>
|
||||||
/// <param name="collector">Custom users/chats collector. By default, those are collected in properties Users/Chats</param>
|
/// <param name="collector">Custom users/chats collector. By default, those are collected in properties Users/Chats</param>
|
||||||
/// <param name="reentrant"><see langword="true"/> if your <paramref name="onUpdate"/> method can be called again even when last async call didn't return yet</param>
|
/// <param name="reentrant"><see langword="true"/> if your <paramref name="onUpdate"/> method can be called again even when last async call didn't return yet</param>
|
||||||
public UpdateManager(Client client, Func<Update, Task> onUpdate, IDictionary<long, MBoxState> state = null, UserChatCollector collector = null, bool reentrant = false)
|
public UpdateManager(Client client, Func<Update, Task> onUpdate, IDictionary<long, MBoxState> state = null, IPeerCollector collector = null, bool reentrant = false)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_onUpdate = onUpdate;
|
_onUpdate = onUpdate;
|
||||||
if (collector != null)
|
_collector = collector ?? new Extensions.CollectorPeer(Users = [], Chats = []);
|
||||||
_onCollect = collector;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_collector = new() { _users = Users = [], _chats = Chats = [] };
|
|
||||||
_onCollect = _collector.UserOrChat;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == null)
|
if (state == null)
|
||||||
_local = new() { [L_SEQ] = new() { access_hash = UndefinedSeqDate }, [L_QTS] = new(), [L_PTS] = new() };
|
_local = new() { [L_SEQ] = new() { access_hash = UndefinedSeqDate }, [L_QTS] = new(), [L_PTS] = new() };
|
||||||
|
|
@ -85,7 +76,7 @@ namespace WTelegram
|
||||||
if (_local[L_PTS].pts != 0) await ResyncState();
|
if (_local[L_PTS].pts != 0) await ResyncState();
|
||||||
break;
|
break;
|
||||||
case User user when user.flags.HasFlag(User.Flags.self):
|
case User user when user.flags.HasFlag(User.Flags.self):
|
||||||
if (Users != null) Users[user.id] = user;
|
_collector.Collect([user]);
|
||||||
goto newSession;
|
goto newSession;
|
||||||
case NewSessionCreated when _client.User != null:
|
case NewSessionCreated when _client.User != null:
|
||||||
newSession:
|
newSession:
|
||||||
|
|
@ -130,15 +121,14 @@ namespace WTelegram
|
||||||
var now = _lastUpdateStamp = DateTime.UtcNow;
|
var now = _lastUpdateStamp = DateTime.UtcNow;
|
||||||
var updateList = updates.UpdateList;
|
var updateList = updates.UpdateList;
|
||||||
if (updates is UpdateShortSentMessage sent)
|
if (updates is UpdateShortSentMessage sent)
|
||||||
updateList = new[] { new UpdateNewMessage { pts = sent.pts, pts_count = sent.pts_count, message = new Message {
|
updateList = [new UpdateNewMessage { pts = sent.pts, pts_count = sent.pts_count, message = new Message {
|
||||||
flags = (Message.Flags)sent.flags,
|
flags = (Message.Flags)sent.flags,
|
||||||
id = sent.id, date = sent.date, entities = sent.entities, media = sent.media, ttl_period = sent.ttl_period,
|
id = sent.id, date = sent.date, entities = sent.entities, media = sent.media, ttl_period = sent.ttl_period,
|
||||||
} } };
|
} }];
|
||||||
else if (Users != null)
|
else if (updates is UpdateShortMessage usm && !_collector.HasUser(usm.user_id))
|
||||||
if (updates is UpdateShortMessage usm && !Users.ContainsKey(usm.user_id))
|
RaiseCollect(await _client.Updates_GetDifference(usm.pts - usm.pts_count, usm.date, 0));
|
||||||
(await _client.Updates_GetDifference(usm.pts - usm.pts_count, usm.date, 0)).UserOrChat(_collector);
|
else if (updates is UpdateShortChatMessage uscm && (!_collector.HasUser(uscm.from_id) || !_collector.HasChat(uscm.chat_id)))
|
||||||
else if (updates is UpdateShortChatMessage uscm && (!Users.ContainsKey(uscm.from_id) || !Chats.ContainsKey(uscm.chat_id)))
|
RaiseCollect(await _client.Updates_GetDifference(uscm.pts - uscm.pts_count, uscm.date, 0));
|
||||||
(await _client.Updates_GetDifference(uscm.pts - uscm.pts_count, uscm.date, 0)).UserOrChat(_collector);
|
|
||||||
|
|
||||||
bool ptsChanged = false, gotUPts = false;
|
bool ptsChanged = false, gotUPts = false;
|
||||||
int seq = 0;
|
int seq = 0;
|
||||||
|
|
@ -261,10 +251,12 @@ namespace WTelegram
|
||||||
Log?.Invoke(2, $"({mbox_id,10}, new +{pts_count}->{pts,-6}) {update,-30} First appearance of MBox {ExtendedLog(update)}");
|
Log?.Invoke(2, $"({mbox_id,10}, new +{pts_count}->{pts,-6}) {update,-30} First appearance of MBox {ExtendedLog(update)}");
|
||||||
else if (local.access_hash == -1) // no valid access_hash for this channel, so just raise this update
|
else if (local.access_hash == -1) // no valid access_hash for this channel, so just raise this update
|
||||||
Log?.Invoke(3, $"({mbox_id,10}, {local.pts,6}+{pts_count}->{pts,-6}) {update,-30} No access_hash to recover {ExtendedLog(update)}");
|
Log?.Invoke(3, $"({mbox_id,10}, {local.pts,6}+{pts_count}->{pts,-6}) {update,-30} No access_hash to recover {ExtendedLog(update)}");
|
||||||
|
else if (local.pts + pts_count - pts >= 0)
|
||||||
|
getDiffSuccess = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log?.Invoke(1, $"({mbox_id,10}, {local.pts,6}+{pts_count}->{pts,-6}) {update,-30} Calling GetDifference {ExtendedLog(update)}");
|
Log?.Invoke(1, $"({mbox_id,10}, {local.pts,6}+{pts_count}->{pts,-6}) {update,-30} Calling GetDifference {ExtendedLog(update)}");
|
||||||
getDiffSuccess = await GetDifference(mbox_id, pts - pts_count, local);
|
getDiffSuccess = await GetDifference(mbox_id, pts, local);
|
||||||
}
|
}
|
||||||
if (!getDiffSuccess) // no getDiff => just raise received pending updates in order
|
if (!getDiffSuccess) // no getDiff => just raise received pending updates in order
|
||||||
{
|
{
|
||||||
|
|
@ -382,6 +374,11 @@ namespace WTelegram
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log?.Invoke(4, $"GetDifference({mbox_id}, {local.pts}->{expected_pts}) raised {ex}");
|
Log?.Invoke(4, $"GetDifference({mbox_id}, {local.pts}->{expected_pts}) raised {ex}");
|
||||||
|
if (ex.Message == "PERSISTENT_TIMESTAMP_INVALID") // oh boy, we're lost!
|
||||||
|
if (mbox_id <= 0)
|
||||||
|
await HandleDifference(null, null, await _client.Updates_GetState(), null);
|
||||||
|
else if ((await _client.Channels_GetFullChannel(await GetInputChannel(mbox_id, local))).full_chat is ChannelFull full)
|
||||||
|
local.pts = full.pts;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -441,6 +438,14 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RaiseCollect(Updates_DifferenceBase diff)
|
||||||
|
{
|
||||||
|
if (diff is Updates_DifferenceSlice uds)
|
||||||
|
RaiseCollect(uds.users, uds.chats);
|
||||||
|
else if (diff is Updates_Difference ud)
|
||||||
|
RaiseCollect(ud.users, ud.chats);
|
||||||
|
}
|
||||||
|
|
||||||
private void RaiseCollect(Dictionary<long, User> users, Dictionary<long, ChatBase> chats)
|
private void RaiseCollect(Dictionary<long, User> users, Dictionary<long, ChatBase> chats)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -449,11 +454,12 @@ namespace WTelegram
|
||||||
if (chat is Channel channel && !channel.flags.HasFlag(Channel.Flags.min))
|
if (chat is Channel channel && !channel.flags.HasFlag(Channel.Flags.min))
|
||||||
if (_local.TryGetValue(channel.id, out var local))
|
if (_local.TryGetValue(channel.id, out var local))
|
||||||
local.access_hash = channel.access_hash;
|
local.access_hash = channel.access_hash;
|
||||||
_onCollect(users, chats);
|
_collector.Collect(users.Values);
|
||||||
|
_collector.Collect(chats.Values);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log?.Invoke(4, $"onCollect({users?.Count},{chats?.Count}) raised {ex}");
|
Log?.Invoke(4, $"Collect({users?.Count},{chats?.Count}) raised {ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,6 +522,14 @@ namespace WTelegram
|
||||||
/// <summary>returns a <see cref="User"/> or <see cref="ChatBase"/> for the given Peer</summary>
|
/// <summary>returns a <see cref="User"/> or <see cref="ChatBase"/> for the given Peer</summary>
|
||||||
public IPeerInfo UserOrChat(Peer peer) => peer?.UserOrChat(Users, Chats);
|
public IPeerInfo UserOrChat(Peer peer) => peer?.UserOrChat(Users, Chats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IPeerCollector
|
||||||
|
{
|
||||||
|
void Collect(IEnumerable<User> users);
|
||||||
|
void Collect(IEnumerable<ChatBase> chats);
|
||||||
|
bool HasUser(long id);
|
||||||
|
bool HasChat(long id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace TL
|
namespace TL
|
||||||
|
|
@ -530,7 +544,7 @@ namespace TL
|
||||||
/// <param name="statePath">Resume session by recovering all updates that occured since the state saved in this file</param>
|
/// <param name="statePath">Resume session by recovering all updates that occured since the state saved in this file</param>
|
||||||
/// <param name="collector">Custom users/chats collector. By default, those are collected in properties Users/Chats</param>
|
/// <param name="collector">Custom users/chats collector. By default, those are collected in properties Users/Chats</param>
|
||||||
/// <param name="reentrant"><see langword="true"/> if your <paramref name="onUpdate"/> method can be called again even when last async call didn't return yet</param>
|
/// <param name="reentrant"><see langword="true"/> if your <paramref name="onUpdate"/> method can be called again even when last async call didn't return yet</param>
|
||||||
public static UpdateManager WithUpdateManager(this Client client, Func<TL.Update, Task> onUpdate, string statePath, UserChatCollector collector = null, bool reentrant = false)
|
public static UpdateManager WithUpdateManager(this Client client, Func<TL.Update, Task> onUpdate, string statePath, IPeerCollector collector = null, bool reentrant = false)
|
||||||
=> new(client, onUpdate, UpdateManager.LoadState(statePath), collector, reentrant);
|
=> new(client, onUpdate, UpdateManager.LoadState(statePath), collector, reentrant);
|
||||||
|
|
||||||
/// <summary>Manager ensuring that you receive Telegram updates in correct order, without missing any</summary>
|
/// <summary>Manager ensuring that you receive Telegram updates in correct order, without missing any</summary>
|
||||||
|
|
@ -538,7 +552,7 @@ namespace TL
|
||||||
/// <param name="state">(optional) Resume session by recovering all updates that occured since this state</param>
|
/// <param name="state">(optional) Resume session by recovering all updates that occured since this state</param>
|
||||||
/// <param name="collector">Custom users/chats collector. By default, those are collected in properties Users/Chats</param>
|
/// <param name="collector">Custom users/chats collector. By default, those are collected in properties Users/Chats</param>
|
||||||
/// <param name="reentrant"><see langword="true"/> if your <paramref name="onUpdate"/> method can be called again even when last async call didn't return yet</param>
|
/// <param name="reentrant"><see langword="true"/> if your <paramref name="onUpdate"/> method can be called again even when last async call didn't return yet</param>
|
||||||
public static UpdateManager WithUpdateManager(this Client client, Func<TL.Update, Task> onUpdate, IDictionary<long, UpdateManager.MBoxState> state = null, UserChatCollector collector = null, bool reentrant = false)
|
public static UpdateManager WithUpdateManager(this Client client, Func<TL.Update, Task> onUpdate, IDictionary<long, UpdateManager.MBoxState> state = null, IPeerCollector collector = null, bool reentrant = false)
|
||||||
=> new(client, onUpdate, state, collector, reentrant);
|
=> new(client, onUpdate, state, collector, reentrant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
</ItemGroup>-->
|
</ItemGroup>-->
|
||||||
|
|
||||||
<ItemGroup Condition="$(DefineConstants.Contains('MTPG'))" >
|
<ItemGroup Condition="$(DefineConstants.Contains('MTPG'))" >
|
||||||
<ProjectReference Include="..\generator\MTProtoGenerator.csproj" OutputItemType="Analyzer" />
|
<ProjectReference Include="..\generator\MTProtoGenerator.csproj" OutputItemType="Analyzer" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue