diff --git a/EXAMPLES.md b/EXAMPLES.md
index 3b64eca..f663838 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -469,6 +469,9 @@ var msg = history.Messages[0] as Message; // last message of source chat
// • Forward the message (only the source message id is necessary)
await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat);
-// • Copy the message (without the "Forwarded" header)
+// • Copy the message without the "Forwarded" header (only the source message id is necessary)
+await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat, drop_author: true);
+
+// • Alternative solution to copy the message (the full message is needed)
await client.SendMessageAsync(to_chat, msg.message, msg.media?.ToInputMedia(), entities: msg.entities);
-```
\ No newline at end of file
+```
diff --git a/src/Client.cs b/src/Client.cs
index abfcea9..10c61c4 100644
--- a/src/Client.cs
+++ b/src/Client.cs
@@ -1180,7 +1180,7 @@ namespace WTelegram
try
{
using var memStream = new MemoryStream(1024);
- using var writer = new BinaryWriter(memStream, Encoding.UTF8);
+ using var writer = new BinaryWriter(memStream);
writer.Write(0); // int32 payload_len (to be patched with payload length)
if (_dcSession.AuthKeyID == 0) // send unencrypted message
@@ -1196,7 +1196,7 @@ namespace WTelegram
else
{
using var clearStream = new MemoryStream(1024);
- using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8);
+ using var clearWriter = new BinaryWriter(clearStream);
clearWriter.Write(_dcSession.AuthKey, 88, 32);
clearWriter.Write(_dcSession.Salt); // int64 salt
clearWriter.Write(_dcSession.Id); // int64 session_id
@@ -1210,7 +1210,7 @@ namespace WTelegram
clearWriter.WriteTLObject(msg); // bytes message_data
int clearLength = (int)clearStream.Length - 32; // length before padding (= 32 + message_data_length)
int padding = (0x7FFFFFF0 - clearLength) % 16;
- padding += _random.Next(1, 64) * 16; // MTProto 2.0 padding must be between 12..1024 with total length divisible by 16
+ padding += _random.Next(2, 16) * 16; // MTProto 2.0 padding must be between 12..1024 with total length divisible by 16
clearStream.SetLength(32 + clearLength + padding);
byte[] clearBuffer = clearStream.GetBuffer();
BinaryPrimitives.WriteInt32LittleEndian(clearBuffer.AsSpan(60), clearLength - 32); // patch message_data_length
diff --git a/src/Encryption.cs b/src/Encryption.cs
index 774df0d..8cec009 100644
--- a/src/Encryption.cs
+++ b/src/Encryption.cs
@@ -62,7 +62,7 @@ namespace WTelegram
{
//4.1) RSA_PAD(data, server_public_key)
using var clearStream = new MemoryStream(256);
- using var writer = new BinaryWriter(clearStream, Encoding.UTF8);
+ using var writer = new BinaryWriter(clearStream);
byte[] aes_key = new byte[32], zero_iv = new byte[32];
var n = BigEndianInteger(publicKey.n);
do
@@ -108,16 +108,17 @@ namespace WTelegram
if (serverDHinnerData.server_nonce != resPQ.server_nonce) throw new ApplicationException("Server Nonce mismatch");
var g_a = BigEndianInteger(serverDHinnerData.g_a);
var dh_prime = BigEndianInteger(serverDHinnerData.dh_prime);
- ValidityChecks(dh_prime, serverDHinnerData.g);
+ CheckGoodPrime(dh_prime, serverDHinnerData.g);
session.LastSentMsgId = 0;
session.ServerTicksOffset = (serverDHinnerData.server_time - localTime).Ticks;
Helpers.Log(1, $"Time offset: {session.ServerTicksOffset} | Server: {serverDHinnerData.server_time.TimeOfDay} UTC | Local: {localTime.TimeOfDay} UTC");
//6)
- var bData = new byte[256];
- RNG.GetBytes(bData);
- var b = BigEndianInteger(bData);
+ var salt = new byte[256];
+ RNG.GetBytes(salt);
+ var b = BigEndianInteger(salt);
var g_b = BigInteger.ModPow(serverDHinnerData.g, b, dh_prime);
- ValidityChecksDH(g_a, g_b, dh_prime);
+ CheckGoodGaAndGb(g_a, dh_prime);
+ CheckGoodGaAndGb(g_b, dh_prime);
var clientDHinnerData = new ClientDHInnerData
{
nonce = nonce,
@@ -128,7 +129,7 @@ namespace WTelegram
{
using var clearStream = new MemoryStream(384);
clearStream.Position = 20; // skip SHA1 area (to be patched)
- using var writer = new BinaryWriter(clearStream, Encoding.UTF8);
+ using var writer = new BinaryWriter(clearStream);
writer.WriteTLObject(clientDHinnerData);
int clearLength = (int)clearStream.Length; // length before padding (= 20 + message_data_length)
int paddingToAdd = (0x7FFFFFF0 - clearLength) % 16;
@@ -182,21 +183,20 @@ namespace WTelegram
}
}
- internal static void ValidityChecks(BigInteger p, int g)
+ internal static void CheckGoodPrime(BigInteger p, int g)
{
Helpers.Log(2, "Verifying encryption key safety... (this should happen only once per DC)");
// check that 2^2047 <= p < 2^2048
if (p.GetBitLength() != 2048) throw new ApplicationException("p is not 2048-bit number");
// check that g generates a cyclic subgroup of prime order (p - 1) / 2, i.e. is a quadratic residue mod p.
- BigInteger mod_r;
if (g switch
{
2 => p % 8 != 7,
3 => p % 3 != 2,
4 => false,
- 5 => (mod_r = p % 5) != 1 && mod_r != 4,
- 6 => (mod_r = p % 24) != 19 && mod_r != 23,
- 7 => (mod_r = p % 7) != 3 && mod_r != 5 && mod_r != 6,
+ 5 => (int)(p % 5) is not 1 and not 4,
+ 6 => (int)(p % 24) is not 19 and not 23,
+ 7 => (int)(p % 7) is not 3 and not 5 and not 6,
_ => true,
})
throw new ApplicationException("Bad prime mod 4g");
@@ -227,13 +227,11 @@ namespace WTelegram
0x73, 0x3F, 0xF1, 0x70, 0x2F, 0x52, 0x6C, 0x8E, 0x04, 0xC9, 0xB1, 0xC6, 0xB9, 0xAE, 0x1C, 0xC7, 0x00
})};
- internal static void ValidityChecksDH(BigInteger g_a, BigInteger g_b, BigInteger dh_prime)
+ internal static void CheckGoodGaAndGb(BigInteger g, BigInteger dh_prime)
{
// check that g, g_a and g_b are greater than 1 and less than dh_prime - 1.
// We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
- var l = BigInteger.One << (2048 - 64);
- var r = dh_prime - l;
- if (g_a < l || g_a > r || g_b < l || g_b > r)
+ if (g.GetBitLength() < 2048 - 64 || (dh_prime - g).GetBitLength() < 2048 - 64)
throw new ApplicationException("g^a or g^b is not between 2^{2048-64} and dh_prime - 2^{2048-64}");
}
@@ -405,7 +403,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
var g = new BigInteger(algo.g);
var p = BigEndianInteger(algo.p);
- var validTask = Task.Run(() => ValidityChecks(p, algo.g));
+ var validTask = Task.Run(() => CheckGoodPrime(p, algo.g));
System.Threading.Thread.Sleep(100);
Helpers.Log(3, $"This account has enabled 2FA. A password is needed. {accountPassword.hint}");
diff --git a/src/TL.Schema.cs b/src/TL.Schema.cs
index c930ea4..bcce540 100644
--- a/src/TL.Schema.cs
+++ b/src/TL.Schema.cs
@@ -4699,6 +4699,7 @@ namespace TL
cdn = 0x8,
/// If set, this IP should be used when connecting through a proxy
static_ = 0x10,
+ /// If set, clients must connect using only the specified port, without trying any other port.
this_port_only = 0x20,
/// Field has a value
has_secret = 0x400,
@@ -4832,7 +4833,7 @@ namespace TL
has_static_maps_provider = 0x1000,
/// Whether pfs was used
pfs_enabled = 0x2000,
- /// Whether to forcefully try connecting using IPv6
+ /// Whether to forcefully connect using IPv6 , even if the client knows that IPv4 is available.
force_try_ipv6 = 0x4000,
/// Field has a value
has_reactions_default = 0x8000,
@@ -12215,7 +12216,6 @@ namespace TL
has_reply_to_peer_id = 0x1,
/// Field has a value
has_reply_to_top_id = 0x2,
- /// Whether this message replies to a scheduled message
reply_to_scheduled = 0x4,
}
}
@@ -13505,7 +13505,7 @@ namespace TL
[TLDef(0x661D4037)]
public class ChatReactionsSome : ChatReactions
{
- /// Allowed reactions
+ /// Allowed set of reactions: the reactions_in_chat_max configuration field indicates the maximum number of reactions that can be specified in this field.
public Reaction[] reactions;
}
diff --git a/src/TL.SchemaFuncs.cs b/src/TL.SchemaFuncs.cs
index d6c90b4..4a02c8c 100644
--- a/src/TL.SchemaFuncs.cs
+++ b/src/TL.SchemaFuncs.cs
@@ -869,7 +869,7 @@ namespace TL
/// Get theme information See Possible codes: 400 (details)
/// Theme format, a string that identifies the theming engines supported by the client
/// Theme
- /// Document ID
+ /// Deprecated: should always be 0
public static Task Account_GetTheme(this Client client, string format, InputThemeBase theme, long document_id)
=> client.Invoke(new Account_GetTheme
{
@@ -1931,7 +1931,7 @@ namespace TL
unsave = unsave,
});
- /// Query an inline bot See Possible codes: -503,400 (details)
+ /// Query an inline bot See Possible codes: 400,-503 (details)
/// The bot to query
/// The currently opened chat
/// The geolocation, if requested
@@ -2042,7 +2042,7 @@ namespace TL
entities = entities,
});
- /// Press an inline callback button and get a callback answer from the bot See Possible codes: -503,400 (details)
+ /// Press an inline callback button and get a callback answer from the bot See Possible codes: 400,-503 (details)
/// Whether this is a "play game" button
/// Where was the inline keyboard sent
/// ID of the Message with the inline keyboard
@@ -2408,7 +2408,7 @@ namespace TL
/// Whether to move used stickersets to top, see here for more info on this flag »
/// The destination chat
/// The message to reply to
- /// The medias to send
+ /// The medias to send: note that they must be separately uploaded using messages.uploadMedia first, using raw inputMediaUploaded* constructors is not supported.
/// Scheduled message date for scheduled messages
/// Send this message as the specified peer
public static Task Messages_SendMultiMedia(this Client client, InputPeer peer, InputSingleMedia[] multi_media, bool silent = false, bool background = false, bool clear_draft = false, bool noforwards = false, bool update_stickersets_order = false, int? reply_to_msg_id = null, DateTime? schedule_date = null, InputPeer send_as = null)
@@ -3115,7 +3115,7 @@ namespace TL
hash = hash,
});
- /// Change default emoji reaction to use in the quick reaction menu: the value is synced across devices and can be fetched using help.getAppConfig, reactions_default field. See Possible codes: 400 (details)
+ /// Change default emoji reaction to use in the quick reaction menu: the value is synced across devices and can be fetched using help.getConfig, reactions_default field. See Possible codes: 400 (details)
/// New emoji reaction
public static Task Messages_SetDefaultReaction(this Client client, Reaction reaction)
=> client.Invoke(new Messages_SetDefaultReaction