diff --git a/src/TgSharp.Core/TelegramClient.cs b/src/TgSharp.Core/TelegramClient.cs
index 953a6cec..84846969 100644
--- a/src/TgSharp.Core/TelegramClient.cs
+++ b/src/TgSharp.Core/TelegramClient.cs
@@ -8,6 +8,7 @@
using TeleSharp.TL;
using TeleSharp.TL.Account;
using TeleSharp.TL.Auth;
+using TeleSharp.TL.Channels;
using TeleSharp.TL.Contacts;
using TeleSharp.TL.Help;
using TeleSharp.TL.Messages;
@@ -17,6 +18,7 @@
using TgSharp.Core.MTProto.Crypto;
using TgSharp.Core.Network;
using TgSharp.Core.Network.Exceptions;
+using TgSharp.Core.Types;
using TgSharp.Core.Utils;
using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization;
@@ -24,6 +26,11 @@ namespace TgSharp.Core
{
public class TelegramClient : IDisposable
{
+ ///
+ /// When pagination is needed, this is the default page size
+ ///
+ public const int DEFAULT_PAGE_SIZE = 200;
+
private MtProtoSender sender;
private TcpTransport transport;
private string apiHash = String.Empty;
@@ -31,7 +38,7 @@ public class TelegramClient : IDisposable
private Session session;
private List dcOptions;
private TcpClientConnectionHandler handler;
- private DataCenterIPVersion dcIpVersion;
+ private DataCenterIPVersions dcIpVersion;
public Session Session
{
@@ -49,7 +56,7 @@ public Session Session
/// Indicates the preferred IpAddress version to use to connect to a Telegram server
public TelegramClient(int apiId, string apiHash,
ISessionStore store = null, string sessionUserId = "session", TcpClientConnectionHandler handler = null,
- DataCenterIPVersion dcIpVersion = DataCenterIPVersion.Default)
+ DataCenterIPVersions dcIpVersion = DataCenterIPVersions.Default)
{
if (apiId == default(int))
throw new MissingApiConfigurationException("API_ID");
@@ -114,20 +121,20 @@ public TelegramClient(int apiId, string apiHash,
}
IEnumerable dcs;
- if (dcIpVersion == DataCenterIPVersion.OnlyIPv6)
+ if (dcIpVersion == DataCenterIPVersions.OnlyIPv6)
dcs = dcOptions.Where(d => d.Id == dcId && d.Ipv6); // selects only ipv6 addresses
- else if (dcIpVersion == DataCenterIPVersion.OnlyIPv4)
+ else if (dcIpVersion == DataCenterIPVersions.OnlyIPv4)
dcs = dcOptions.Where(d => d.Id == dcId && !d.Ipv6); // selects only ipv4 addresses
else
dcs = dcOptions.Where(d => d.Id == dcId); // any
TLDcOption dc;
- if (dcIpVersion != DataCenterIPVersion.Default)
+ if (dcIpVersion != DataCenterIPVersions.Default)
{
if (!dcs.Any())
throw new Exception($"Telegram server didn't provide us with any IPAddress that matches your preferences. If you chose OnlyIPvX, try switch to PreferIPvX instead.");
dcs = dcs.OrderBy(d => d.Ipv6);
- dc = dcIpVersion == DataCenterIPVersion.PreferIPv4 ? dcs.First() : dcs.Last(); // ipv4 addresses are at the beginning of the list because it was ordered
+ dc = dcIpVersion == DataCenterIPVersions.PreferIPv4 ? dcs.First() : dcs.Last(); // ipv4 addresses are at the beginning of the list because it was ordered
}
else
dc = dcs.First();
@@ -284,7 +291,7 @@ public bool IsUserAuthorized()
public async Task UpdateUsernameAsync(string username, CancellationToken token = default(CancellationToken))
{
- var req = new TLRequestUpdateUsername { Username = username };
+ var req = new TeleSharp.TL.Account.TLRequestUpdateUsername { Username = username };
return await SendAuthenticatedRequestAsync(req, token)
.ConfigureAwait(false);
@@ -292,7 +299,7 @@ public bool IsUserAuthorized()
public async Task CheckUsernameAsync(string username, CancellationToken token = default(CancellationToken))
{
- var req = new TLRequestCheckUsername { Username = username };
+ var req = new TeleSharp.TL.Account.TLRequestCheckUsername { Username = username };
return await SendAuthenticatedRequestAsync(req, token)
.ConfigureAwait(false);
@@ -454,6 +461,306 @@ await sender.SendPingAsync(token)
.ConfigureAwait(false);
}
+ ///
+ /// Authenticates a Bot
+ ///
+ /// The token of the bot to authenticate
+ ///
+ /// The TLUser descriptor
+ public async Task MakeAuthBotAsync(string botAuthToken, CancellationToken token = default(CancellationToken))
+ {
+ if (String.IsNullOrWhiteSpace(botAuthToken))
+ {
+ throw new ArgumentNullException(nameof(botAuthToken));
+ }
+
+ var request = new TLRequestImportBotAuthorization() { BotAuthToken = botAuthToken, ApiId = apiId, ApiHash = apiHash };
+
+ await RequestWithDcMigration(request, token).ConfigureAwait(false);
+
+ OnUserAuthenticated(((TLUser)((TLAuthorization)request.Response).User));
+ return ((TLUser)((TLAuthorization)request.Response).User);
+ }
+
+ ///
+ /// Gets the full information of a specified chat
+ ///
+ /// The ID of the chat we want the info of
+ ///
+ ///
+ public async Task GetFullChat(int chatId, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestGetFullChat() { ChatId = chatId };
+ var fchat = await SendRequestAsync(req, token).ConfigureAwait(false);
+
+ return fchat;
+ }
+
+ ///
+ /// Gets the list of chats and channels opened by the authenticated user.
+ /// Throws an exception if the authenticated user is a bot.
+ ///
+ ///
+ /// The list of chats opened by the authenticated user
+ public async Task GetAllChats(CancellationToken token = default(CancellationToken))
+ {
+ return await GetAllChats(null, token);
+ }
+
+ ///
+ /// Gets the list of chats and channels opened by the authenticated user except the passed ones.
+ /// Throws an exception if the authenticated user is a bot.
+ ///
+ /// The IDs of the chats to be returned
+ ///
+ /// The list of chats opened by the authenticated user
+ public async Task GetAllChats(int[] exceptdIds, CancellationToken token = default(CancellationToken))
+ {
+ var ichats = new TeleSharp.TL.TLVector(); // we can't pass a null argument to the TLRequestGetChats
+ if (exceptdIds != null)
+ foreach (var id in exceptdIds)
+ ichats.Add(id);
+ var chats = await SendRequestAsync(new TLRequestGetAllChats() { ExceptIds = ichats }, token).ConfigureAwait(false);
+ return chats as TLChats;
+ }
+
+ ///
+ /// Gets the information about a channel
+ ///
+ /// The channel to get the info of
+ ///
+ ///
+ public async Task GetFullChannel(TLChannel channel, CancellationToken token = default(CancellationToken))
+ {
+ if (channel == null) return null;
+ return await GetFullChannel(channel.Id, (long)channel.AccessHash, token).ConfigureAwait(false);
+ }
+
+ ///
+ /// Gets the information about a channel
+ ///
+ /// The ID of the channel
+ ///
+ ///
+ public async Task GetFullChannel(int channelId, long accessHash, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestGetFullChannel() { Channel = new TLInputChannel() { ChannelId = channelId, AccessHash = accessHash } };
+ var fchat = await SendRequestAsync(req, token).ConfigureAwait(false);
+
+ return fchat;
+ }
+
+ ///
+ /// Gets the channels having the supplied IDs
+ ///
+ /// The IDs of the channels to be retrieved
+ ///
+ ///
+ public async Task GetChannels(int[] channelIds, CancellationToken token = default(CancellationToken))
+ {
+ var channels = new TLVector(); // we can't pass a null argument to the TLRequestGetChats
+ if (channelIds != null)
+ foreach (var channelId in channelIds)
+ channels.Add(new TLInputChannel() { ChannelId = channelId });
+ var req = new TLRequestGetChannels() { Id = channels };
+ var fchat = await SendRequestAsync(req, token).ConfigureAwait(false);
+
+ return fchat;
+ }
+
+ ///
+ /// Gets the participants of the channel having the supplied type.
+ /// The method will auto-paginate results and return all the participants
+ ///
+ /// The TLChannel whose participants are requested
+ /// The index to start fetching from. -1 will automatically fetch all the results
+ /// How many results to be fetch on each iteration.
+ /// Values smaller than 0 are ignored. If stIdx is set, a number of results smaller than pageSize might be returned by Telegram.
+ /// The type of the participants to get. Choose Recents not to filter
+ ///
+ ///
+ public async Task GetParticipants(TLChannel channel, int stIdx = -1, int pageSize = -1, ParticipantFilterTypes partType = ParticipantFilterTypes.Recents, CancellationToken token = default(CancellationToken))
+ {
+ if (channel == null) return null;
+ return await GetParticipants(channel.Id, (long)channel.AccessHash, stIdx, pageSize, partType, token).ConfigureAwait(false);
+ }
+
+ ///
+ /// Gets the participants of the channel having the supplied type.
+ /// The method will auto-paginate results and return all the participants
+ ///
+ /// The id of the channel whose participants are requested
+ /// The access hash of the channel whose participants are requested
+ /// The index to start fetching from. -1 will automatically fetch all the results
+ /// How many results to be fetch on each iteration.
+ /// Values smaller than 0 are ignored. If stIdx is set, a number of results smaller than pageSize might be returned by Telegram.
+ /// The type of the participants to get. Choose Recents not to filter
+ ///
+ ///
+ public async Task GetParticipants(int channelId, long accessHash, int stIdx = -1, int pageSize = -1, ParticipantFilterTypes partType = ParticipantFilterTypes.Recents, CancellationToken token = default(CancellationToken))
+ {
+ TLAbsChannelParticipantsFilter filter;
+ switch (partType)
+ {
+ case ParticipantFilterTypes.Admins:
+ filter = new TLChannelParticipantsAdmins();
+ break;
+
+ case ParticipantFilterTypes.Kicked:
+ filter = new TLChannelParticipantsKicked();
+ break;
+
+ case ParticipantFilterTypes.Bots:
+ filter = new TLChannelParticipantsBots();
+ break;
+
+ case ParticipantFilterTypes.Recents:
+ filter = new TLChannelParticipantsRecent();
+ break;
+
+ //case ParticipantFilterTypes.Banned:
+ // filter = new TLChannelParticipantsBanned();
+ // break;
+
+ //// case ParticipantFilterTypes.Restricted:
+ //// filter = new tlchannelparticipants
+
+ //case ParticipantFilterTypes.Contacts:
+ // filter = new TLChannelParticipantsContacts();
+ // break;
+
+ //case ParticipantFilterTypes.Search:
+ // filter = new TLChannelParticipantsSearch();
+ // break;
+
+ default:
+ throw new NotImplementedException($"{partType} not implemented yet");
+ }
+
+ int total = 0;
+ int found = stIdx < 0 ? 0 : stIdx;
+ pageSize = pageSize < 0 ? DEFAULT_PAGE_SIZE : pageSize;
+
+ TLChannelParticipants ret = new TLChannelParticipants();
+ ret.Participants = new TLVector();
+ ret.Users = new TLVector();
+
+ do
+ {
+ var req = new TLRequestGetParticipants()
+ {
+ Channel = new TLInputChannel()
+ {
+ ChannelId = channelId,
+ AccessHash = accessHash
+ },
+ Filter = filter,
+ Offset = found,
+ Limit = pageSize
+ };
+ var fchat = await SendRequestAsync(req, token).ConfigureAwait(false);
+ total = fchat.Count;
+ found += fchat.Participants.Count;
+ foreach (var p in fchat.Participants)
+ ret.Participants.Add(p);
+ foreach (var u in fchat.Users)
+ ret.Users.Add(u);
+ } while (found < total && stIdx == -1);
+ ret.Count = ret.Participants.Count;
+ return ret;
+ }
+
+ ///
+ /// Invites the passed users to the specified channel
+ ///
+ /// The descriptor of the channel to invite the users to
+ ///
+ ///
+ public async Task InviteToChannel(TLChannel channel, int[] users, CancellationToken token = default(CancellationToken))
+ {
+ return await InviteToChannel(channel.Id, (long)channel.AccessHash, users, token);
+ }
+
+ ///
+ /// Invites the passed users to the specified channel
+ ///
+ /// The id of the channel to invite the users to
+ /// The access hash of the channel to invite the users to
+ ///
+ ///
+ public async Task InviteToChannel(int channelId, long accessHash, int[] users, CancellationToken token = default(CancellationToken))
+ {
+ TLVector absUsers = new TLVector();
+ foreach (var user in users)
+ absUsers.Add(new TLInputUser() { UserId = user });
+
+ var req = new TLRequestInviteToChannel()
+ {
+ Channel = new TLInputChannel()
+ {
+ ChannelId = channelId,
+ AccessHash = accessHash
+ },
+ Users = absUsers
+ };
+ var updates = await SendRequestAsync(req, token).ConfigureAwait(false);
+
+ return updates;
+ }
+
+ ///
+ /// Joins a chat invite and returns the updates
+ /// This method can't be used by a bot.
+ /// For a list of possible errors
+ ///
+ /// hash from t.me/joinchat/hash
+ /// The updates
+ public async Task ImportChatInvite(string hash, CancellationToken token = default(CancellationToken))
+ {
+ TLRequestImportChatInvite req = new TLRequestImportChatInvite()
+ {
+ Hash = hash
+ };
+ var updates = await SendRequestAsync(req, token).ConfigureAwait(false);
+ return updates;
+ }
+
+ ///
+ /// Resolves a given Telegram username to the corresponding object. A username can be a person's username or a group or a channel name.
+ ///
+ /// The telegram @username to resolve, without the @. It can be a person's username or a group or a channel name.
+ ///
+ ///
+ public async Task ResolveUsername(string username, CancellationToken token = default(CancellationToken))
+ {
+ if (string.IsNullOrWhiteSpace(username)) return null;
+
+ TLRequestResolveUsername req = new TLRequestResolveUsername()
+ {
+ Username = username
+ };
+ var ret = await SendRequestAsync(req, token).ConfigureAwait(false);
+ return ret;
+ }
+
+ ///
+ /// Checks whether or not the authenticated user has joined the chat already
+ ///
+ /// Invite hash in t.me/joinchat/hash
+ ///
+ ///
+ public async Task CheckChatInvite(string hash, CancellationToken token = default(CancellationToken))
+ {
+ if (string.IsNullOrWhiteSpace(hash)) return null;
+
+ TLRequestCheckChatInvite req = new TLRequestCheckChatInvite()
+ {
+ Hash = hash
+ };
+ var ret = await SendAuthenticatedRequestAsync(req, token).ConfigureAwait(false);
+ return ret;
+ }
+
private void OnUserAuthenticated(TLUser TLUser)
{
session.TLUser = TLUser;
@@ -466,7 +773,7 @@ public bool IsConnected
{
get
{
- if (transport == null)
+ if (transport == null || sender == null)
return false;
return transport.IsConnected;
}
diff --git a/src/TgSharp.Core/TgSharp.Core.csproj b/src/TgSharp.Core/TgSharp.Core.csproj
index da7dd3f0..6c1ab0b2 100644
--- a/src/TgSharp.Core/TgSharp.Core.csproj
+++ b/src/TgSharp.Core/TgSharp.Core.csproj
@@ -45,7 +45,7 @@
-
+
@@ -71,11 +71,12 @@
+
+
-
diff --git a/src/TgSharp.Core/DataCenterIPVersion.cs b/src/TgSharp.Core/Types/DataCenterIPVersions.cs
similarity index 92%
rename from src/TgSharp.Core/DataCenterIPVersion.cs
rename to src/TgSharp.Core/Types/DataCenterIPVersions.cs
index 4676ee11..cf21765d 100644
--- a/src/TgSharp.Core/DataCenterIPVersion.cs
+++ b/src/TgSharp.Core/Types/DataCenterIPVersions.cs
@@ -1,10 +1,10 @@
-namespace TgSharp.Core
+namespace TgSharp.Core.Types
{
///
/// When the Telegram server responds with a set of addresses to connect to, DataCenterIPVersion indicates a preference
/// for how to choose the IP address to connect to
///
- public enum DataCenterIPVersion
+ public enum DataCenterIPVersions
{
///
/// Picks the first available address passed by Telegram
@@ -27,4 +27,6 @@ public enum DataCenterIPVersion
///
PreferIPv6,
}
+
+
}
diff --git a/src/TgSharp.Core/Types/ParticipantFilterTypes.cs b/src/TgSharp.Core/Types/ParticipantFilterTypes.cs
new file mode 100644
index 00000000..b14c6f7a
--- /dev/null
+++ b/src/TgSharp.Core/Types/ParticipantFilterTypes.cs
@@ -0,0 +1,16 @@
+namespace TgSharp.Core.Types
+{
+ public enum ParticipantFilterTypes
+ {
+ Recents,
+ Restricted,
+ Admins,
+ Bots,
+ Search,
+ Contacts,
+ Kicked,
+ Banned
+ }
+
+
+}