Опис двадцяти моїх дрібних фрілансерских проєктів 2016-го року.
- 1. Payment Gateway до системи Paymaster на VB.NET
- 2. ASP.NET API до WHMCS.
- 3. Об'єктна membership-надбудова.
- 4. Сайт з peer-to-peer video-комунікаціями.
- 5. Десктопна прога автоматизації роботи фірми.
- 6. Розсилка SMS за допомогою GSM-передавачів GoIP та за допомогою интернет-комунікацій (WhatsApp на емуляторі Android).
- 7. Проєкт Шаровоз.
- 8. Сайт для мобільників.
- 9. Ще п'ять моїх проєктів (адміністрування серверів, конвертация даних MySQL, виготовлення відео та проект на Visual Basic 6).
- 10. Два моїх останніх проєкта 2016-го року для USA (прога для медицини та торговельный робот).
- 11. Декілька тестових проєктів для різноманітних фірм.
- 12. Проги по натхненню.
1. Payment Gateway до системи Paymaster на VB.NET
Почнемо з проєкту Payment Gateway до системи Paymaster. Це звичайній Payment Gateway, яких я написав безліч за своє життя, наприклад ось тут якісь шлюзи описані в мене - Шлюзы к платежным системам интернет-денег. Незважаючи на те, что опис протокола цієї системи має 10 документів ( (1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ), код системи простий, принаймні у мене він вийшов простий. Цей шлюз має чотири хандлера, головний смисловий хандлер виглядає ось так:
Зверніть увагу, що більшість кода тут - це отладка у зв'язку з неохідністю емуляції PHP-перетворювань:
1: <%@ WebHandler Language="VB" Class="Paymaster_Payment" %>
2:
3: Imports System
4: Imports System.Web
5:
6: 'второй вход https://paymaster.ru/Partners/ru/docs/protocol
7: 'Payment Notification - это единственный запрос, при обработке которого продавцу необходимо учитывать принятый платеж
8: '
9: 'Paymaster_Payment : LMI_MERCHANT_ID=65a755b6-5d3d-4700-b4a1-13ac559d6fc2
10: '&LMI_PAYMENT_SYSTEM=3
11: '&LMI_CURRENCY=RUB
12: '&LMI_PAYMENT_AMOUNT=10.00
13: '&LMI_PAYMENT_NO=897ad9a1-64d8-40ce-ba3d-2459e066552a
14: '&LMI_PAYMENT_DESC=28d14d4f-362c-4638-a410-7cadce308479
15: '&LMI_SYS_PAYMENT_DATE=2016-06-20T12%3a34%3a01
16: '&LMI_SYS_PAYMENT_ID=53121046
17: '&LMI_PAYER_COUNTRY=RU
18: '&LMI_PAID_AMOUNT=10.00
19: '&LMI_PAID_CURRENCY=RUB
20: '&LMI_SIM_MODE=0
21: '&LMI_PAYER_IDENTIFIER=914578691977
22: '&LMI_PAYMENT_METHOD=Test
23: '&LMI_PAYER_PASSPORT_COUNTRY=RU
24: '&LMI_PAYER_IP_ADDRESS=87.249.23.92
25: '&LMI_PAYMENT_REF=836
26: '&LMI_HASH=LCZYl0HgP%2b0O455KgEYiFQ%3d%3d
27:
28:
29:
30: Public Class Paymaster_Payment : Implements IHttpHandler
31:
32: Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
33: Try
34: Dim PostPRM As NameValueCollection = PaymasterGate.CommonRequestCheck(context)
35: If PostPRM Is Nothing Then
36: 'непонятный глюк - не удалось прочитать ни GET, ни POST-параметры
37: Dim Err0 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
38: "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Paymaster_Payment : NoPRM')")
39: Err0.Select(New DataSourceSelectArguments)
40: '
41: context.Response.ContentType = "text/plain"
42: context.Response.Write("Bad request")
43: Exit Sub
44: End If
45:
46: '
47: Dim PaymentNumber As String = PostPRM("LMI_PAYMENT_NO").ToString
48: Dim UserID As String = PostPRM("LMI_PAYMENT_DESC").ToString
49: Dim SYS_PAYMENT_DATE As String = PostPRM("LMI_SYS_PAYMENT_DATE").ToString
50: Dim SYS_PAYMENT_ID As String = PostPRM("LMI_SYS_PAYMENT_ID").ToString
51: Dim SIM_MODE As String = "" 'PostPRM("LMI_SIM_MODE").ToString - рабочий режим
52: Dim PAID_AMOUNT As String = PostPRM("LMI_PAID_AMOUNT").ToString
53: Dim PAYMENT_AMOUNT As String = PostPRM("LMI_PAYMENT_AMOUNT").ToString 'заказанная
54: Dim CURRENCY As String = PostPRM("LMI_CURRENCY").ToString
55: Dim PAID_CURRENCY As String = PostPRM("LMI_PAID_CURRENCY").ToString
56: Dim PAYER_IDENTIFIER As String = PostPRM("LMI_PAYER_IDENTIFIER").ToString
57: Dim PAYER_COUNTRY As String = "NO"
58: If PostPRM("LMI_PAYER_COUNTRY") IsNot Nothing Then PAYER_COUNTRY = PostPRM("LMI_PAYER_COUNTRY").ToString()
59: Dim PAYER_PASSPORT_COUNTRY As String = "NO"
60: If PostPRM("LMI_PAYER_PASSPORT_COUNTRY") IsNot Nothing Then PAYER_PASSPORT_COUNTRY = PostPRM("LMI_PAYER_PASSPORT_COUNTRY").ToString()
61: Dim PAYER_IP_ADDRESS As String = "NO"
62: If PostPRM("LMI_PAYER_IP_ADDRESS") IsNot Nothing Then PAYER_IP_ADDRESS = PostPRM("LMI_PAYER_IP_ADDRESS").ToString()
63: Dim PAYMENT_REF As String = PostPRM("LMI_PAYMENT_REF").ToString
64: Dim PAYMENT_METHOD As String = PostPRM("LMI_PAYMENT_METHOD").ToString
65: Dim PAYMENT_SYSTEM As String = PostPRM("LMI_PAYMENT_SYSTEM").ToString
66: Dim HASH As String = PostPRM("LMI_HASH").ToString
67: '
68: Dim db1 As New ArendaDBDataContext
69: db1.PaymasterPay(New Guid(PaymentNumber), SYS_PAYMENT_DATE, SYS_PAYMENT_ID, "SIM_MODE", PAID_AMOUNT, PAID_CURRENCY, PAYER_IDENTIFIER, PAYER_COUNTRY, PAYER_PASSPORT_COUNTRY, PAYER_IP_ADDRESS, PAYMENT_METHOD, PAYMENT_REF)
70: '
71: 'Инструмент проверки подписи https://paymaster.ru/Partners/ru/utility/generatesign
72: 'Замечание для разработчиков на PHP: если $str - строка параметров, то хеш считается как base64_encode(md5($str, true)). Для PHP версии 4: base64_encode(pack("H*", md5($str)))
73: 'Следующие параметры записываются в одну строчку, разделенные символом ‘;’: LMI_MERCHANT_ID, LMI_PAYMENT_NO, LMI_SYS_PAYMENT_ID, LMI_SYS_PAYMENT_DATE, LMI_PAYMENT_AMOUNT, LMI_CURRENCY, LMI_PAID_AMOUNT, LMI_PAID_CURRENCY, LMI_PAYMENT_SYSTEM, LMI_SIM_MODE
74: '
75: 'Для URL http://localhost:50208/Arenda_4/Paymaster_Payment.ashx?LMI_MERCHANT_ID=65a755b6-5d3d-4700-b4a1-13ac559d6fc2&LMI_PAYMENT_SYSTEM=3&LMI_CURRENCY=RUB&LMI_PAYMENT_AMOUNT=10.00&LMI_PAYMENT_NO=897ad9a1-64d8-40ce-ba3d-2459e066552a&LMI_PAYMENT_DESC=28d14d4f-362c-4638-a410-7cadce308479&LMI_SYS_PAYMENT_DATE=2016-06-20T12%3a34%3a01&LMI_SYS_PAYMENT_ID=53121046&LMI_PAYER_COUNTRY=RU&LMI_PAID_AMOUNT=10.00&LMI_PAID_CURRENCY=RUB&LMI_SIM_MODE=0&LMI_PAYER_IDENTIFIER=914578691977&LMI_PAYMENT_METHOD=Test&LMI_PAYER_PASSPORT_COUNTRY=RU&LMI_PAYER_IP_ADDRESS=87.249.23.92&LMI_PAYMENT_REF=836&LMI_HASH=LCZYl0HgP%2b0O455KgEYiFQ%3d%3d
76: '1. Plan string - 65a755b6-5d3d-4700-b4a1-13ac559d6fc2;897ad9a1-64d8-40ce-ba3d-2459e066552a;53121046;2016-06-20T12:34:01;10.00;RUB;10.00;RUB;3;0;DFA02A86-B028-448D-A3BD-B9749C796626
77: '2. UTF8 bytes - 36 35 61 37 35 35 62 36 2D 35 64 33 64 2D 34 37 30 30 2D 62 34 61 31 2D 31 33 61 63 35 35 39 64 36 66 63 32 3B 38 39 37 61 64 39 61 31 2D 36 34 64 38 2D 34 30 63 65 2D 62 61 33 64 2D 32 34 35 39 65 30 36 36 35 35 32 61 3B 35 33 31 32 31 30 34 36 3B 32 30 31 36 2D 30 36 2D 32 30 54 31 32 3A 33 34 3A 30 31 3B 31 30 2E 30 30 3B 52 55 42 3B 31 30 2E 30 30 3B 52 55 42 3B 33 3B 30 3B 44 46 41 30 32 41 38 36 2D 42 30 32 38 2D 34 34 38 44 2D 41 33 42 44 2D 42 39 37 34 39 43 37 39 36 36 32 36
78: '3. Hash bytes - 2C 26 58 97 41 E0 3F ED 0E E3 9E 4A 80 46 22 15
79: '4. Base64 hash - LCZYl0HgP+0O455KgEYiFQ==
80: '
81: Dim Str1 As String = System.Configuration.ConfigurationManager.AppSettings("PaymasterID").ToString & ";" & _
82: PaymentNumber & ";" & _
83: SYS_PAYMENT_ID & ";" & _
84: SYS_PAYMENT_DATE & ";" & _
85: PAYMENT_AMOUNT & ";" & _
86: PAID_CURRENCY & ";" & _
87: PAID_AMOUNT & ";" & _
88: PAID_CURRENCY & ";" & _
89: PAYMENT_SYSTEM & ";" & _
90: SIM_MODE & ";" & _
91: System.Configuration.ConfigurationManager.AppSettings("PaymasterSecretKey").ToString()
92: Dim UTF8_ByteArr As Byte() = System.Text.UTF8Encoding.UTF8.GetBytes(Str1)
93:
94: Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider()
95: Dim bSignature As Byte() = md5.ComputeHash(UTF8_ByteArr)
96:
97: Dim sbSignature As New StringBuilder()
98: For Each b As Byte In bSignature
99: sbSignature.AppendFormat("{0:x2}", b)
100: Next
101: '
102: Dim Msg2 As String = "Str1(" & Str1 & "), sbSignature(" & sbSignature.ToString() & "), Convert.ToBase64String(bSignature)(" & Convert.ToBase64String(bSignature) & "HASH(" & HASH & ")"
103: Dim Err2 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
104: "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Sign : " & Msg2 & "')")
105: Err2.Select(New DataSourceSelectArguments)
106: '
107: 'Проверка цифровой подписи
108: If Convert.ToBase64String(bSignature) <> HASH Then
109: context.Response.ContentType = "text/plain"
110: context.Response.Write("bad sign")
111: Exit Sub
112: End If
113: 'Подпись совпала
114: Dim X = db1.PaymasterSuccess(New Guid(PaymentNumber)).ToList
115: If X.Count > 0 Then
116: Dim Summ As Decimal
117: Dim AccountNumber As Integer = X(0).AccountNumber
118: Dim Accounttype As Integer = 1 'RUB
119: 'Dim Culture As System.Globalization.CultureInfo = New Globalization.CultureInfo("ru-RU")
120: 'If Decimal.TryParse(PAYMENT_AMOUNT, Globalization.NumberStyles.Currency, Culture, Summ) Then
121: If IsNumeric(GetDecimal(PAYMENT_AMOUNT)) Then
122: Summ = GetDecimal(PAYMENT_AMOUNT)
123: If CURRENCY = "RUB" Then
124: 'принимаются только рублевые платежи и зачисляются только на рублевый счет
125: db1.AddSumm(AccountNumber, New Guid(UserID), Accounttype, Summ)
126: Else
127: db1.AddSumm(AccountNumber, New Guid(UserID), Accounttype, 2)
128: End If
129: Else
130: db1.AddSumm(AccountNumber, New Guid(UserID), Accounttype, 1)
131: End If
132:
133: End If
134: '
135: context.Response.ContentType = "text/plain"
136: context.Response.Write("OK")
137: '
138: Catch ex As Exception
139: Dim Err1 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
140: "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Paymaster_Payment : " & ex.Message & "')")
141: Err1.Select(New DataSourceSelectArguments)
142: '
143: context.Response.ContentType = "text/plain"
144: context.Response.Write(ex.Message)
145: End Try
146:
147: '
148: End Sub
149:
150: Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
151: Get
152: Return False
153: End Get
154: End Property
155:
156: Function GetDecimal(Str1 As String) As String
157: Return Str1.Replace(".00", "").Replace(" ", "")
158: End Function
159:
160: End Class
Невеличкий допоміжний клас виглядає ось так, і знов-таки більшість кода в ньому - це отладка.
1: Imports Microsoft.VisualBasic
2:
3: Public Class PaymasterGate
4:
5: 'Read and Parse Get/Post parameters and common check it
6: Public Shared Function CommonRequestCheck(ByVal context As HttpContext) As NameValueCollection
7: HttpContext.Current.Request.InputStream.Position = 0
8: Dim PostString As String = New System.IO.StreamReader(context.Request.InputStream).ReadToEnd()
9: Dim PostPRM As NameValueCollection = HttpUtility.ParseQueryString(PostString)
10: 'сначала смотрим если есть PostStream
11: If PostPRM.Count > 0 Then
12: '
13: Dim Err0 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
14: "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Post : " & PostPRM.ToString & "')")
15: Err0.Select(New DataSourceSelectArguments)
16: '
17: Return Parse(PostPRM, context)
18: Else
19: 'смотрим, есть ли GetStream
20: Dim GetPRM As NameValueCollection = HttpUtility.ParseQueryString(context.Request.Params.ToString)
21: '
22: Dim Err1 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
23: "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Get : " & GetPRM.ToString & "')")
24: Err1.Select(New DataSourceSelectArguments)
25: '
26: Return Parse(GetPRM, context)
27: End If
28:
29: End Function
30:
31:
32: Public Shared Function Parse(PostPRM As NameValueCollection, ByVal context As HttpContext) As NameValueCollection
33: If PostPRM.Count = 0 Then Return Nothing
34: Dim PaymasterID As String
35: Dim UserID As String
36: Dim PaymentNumber As String
37: Try
38: PaymasterID = PostPRM("LMI_MERCHANT_ID").ToString
39: UserID = PostPRM("LMI_PAYMENT_DESC").ToString
40: PaymentNumber = PostPRM("LMI_PAYMENT_NO").ToString
41: Catch ex As Exception
42: 'есть GET-параметры, но в них только мусор
43: Return Nothing
44: End Try
45: 'параметры найдены, есть смысл их проверять
46: If Not StringExtension.IsGuid(UserID) Or _
47: Not StringExtension.IsGuid(PaymentNumber) Or _
48: PaymasterID.ToString.Trim.ToLower <> System.Configuration.ConfigurationManager.AppSettings("PaymasterID").ToString.ToLower Then
49: context.Response.ContentType = "text/plain"
50: context.Response.Write("no")
51: context.Response.End()
52: Return Nothing
53: Else
54: Return PostPRM
55: End If
56: End Function
57:
58: End Class
Усі процедури, що працюють с грошима, побудовані на транзакціях:
1: ALTER procedure [dbo].[AddSumm]
2: @Account integer,
3: @UserID uniqueIdentifier,
4: @AccountType integer,
5: @Summ money
6: as
7: BEGIN TRY
8: BEGIN TRANSACTION
9: update dbo.Account SET Value=Value+@Summ, SetValueDate=GETDATE() where i= @Account and ToUser=@UserID and ToValueType=@AccountType
10: insert dbo.AccountLog (ToAccount,Date,Value) values (@Account,GETDATE(),@Summ)
11: select * from dbo.Account where i= @Account and ToUser=@UserID and ToValueType=@AccountType
12: COMMIT
13: RETURN 0
14: END TRY
15: BEGIN CATCH
16: DECLARE @ErrorMessage NVARCHAR(4000);
17: DECLARE @ErrorSeverity INT;
18: DECLARE @ErrorState INT;
19: SELECT @ErrorMessage = 'Abort transaction ' + ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
20: RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState );
21: RETURN 1
22: END CATCH
Payment Gateway викликався зі сторінки ASP.NET-сайт таким чином:
1: Partial Class Paymaster
2: Inherits System.Web.UI.Page
3:
4: Dim CurrentUser_ProviderUserKey As String
5: Dim CurrentUser_UserName As String
6:
7: Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
8: If Not IsPostBack Then
9: If User.Identity.IsAuthenticated Then
10: CurrentUser_UserName = User.Identity.Name
11: Dim User1 As MembershipUser = Membership.GetUser(CurrentUser_UserName)
12: If User1 IsNot Nothing Then
13: CurrentUser_ProviderUserKey = User1.ProviderUserKey.ToString
14: Else
15: GoTo defaultUser
16: End If
17: Else
18: defaultUser:
19: CurrentUser_UserName = System.Configuration.ConfigurationManager.AppSettings("Admin_Login").ToString
20: Dim AdmUser1 As MembershipUser = Membership.GetUser(CurrentUser_UserName)
21: If AdmUser1 IsNot Nothing Then
22: CurrentUser_ProviderUserKey = AdmUser1.ProviderUserKey.ToString
23: Else
24: CurrentUser_ProviderUserKey = "28d14d4f-362c-4638-a410-7cadce308479"
25: End If
26: End If
27: '
28: PaymasterWidgetLiteral.Text = "<script type='text/javascript' src='https://paymaster.ru/ru-RU/widget/Basic/1?LMI_MERCHANT_ID=" & _
29: System.Configuration.ConfigurationManager.AppSettings("PaymasterID") & _
30: "&LMI_PAYMENT_AMOUNT=10&LMI_PAYMENT_DESC=" & _
31: CurrentUser_ProviderUserKey.ToString & _
32: "&LMI_PAYER_EMAIL=" & _
33: CurrentUser_UserName & _
34: "&LMI_CURRENCY=RUB&LMI_PAYMENT_NO=" & _
35: Guid.NewGuid.ToString & _
36: "&LMI_SUCCESS_URL=" & _
37: HttpUtility.UrlEncode("http://arenda.votpusk.ru/User_Advance.aspx?User=" & CurrentUser_ProviderUserKey.ToString) & _
38: "&LMI_FAILURE_URL=" & _
39: HttpUtility.UrlEncode("http://arenda.votpusk.ru/User_Advance.aspx?User=" & CurrentUser_ProviderUserKey.ToString) & "'></script>"
40: End If
41: End Sub
42: End Class
Особливість системи у тому, що вона потрібно викликати скрипт, який сам сформує на сторінці кнопки, але скріпт сам теж формує тег <FORMS>, що робить сторінку ASP.NET непрацездатною:
Система пройшла всі тести і зараз чудово працює.
Ще трохи про мої пратежні шлюзи Шлюзы к платежным системам интернет-денег., My payment gateway for Inplat.ru, Liqpay payment gateway.
2. ASP.NET API до WHMCS.
Мій другий проєкт теж був пов'язаний з PHP. Треба було зробити сайт, який буде керувати WHMCS по API, тобто люди вже мали якийсь дизайн (на скрині нище), але не мали, власно-кажучі, діючих алгоритмів.
Тобто задумка цього сайту була у тому, щоб зробити діючу надбудову над WHMCS на ASP.NET. Щоб зробити API на ASP.NET, з якого можна було б керувати великою кількістю хостінг-сістем, побудованних на WHMCS, треба було подолати дуже багато проблем. Починаючи з того, что усі PHP-шні функції не мають аналогів на платформі ASP.NET (нема в них потреби, все робиться інакше), і закінчуючи тим, що взагалі всі функції мали описи на PHP, при чому це були важкі для емуляції функції, наприклад специфічна криптографія.
Я вирішив цю задачку за допомогою специфічної імплементації ось цього свого коду - WCF_CLIENT - клиент Web-сервиса (первая версия).
Для мене це була важка задача, щоб зрозуміти поведінку PHP-середовища, мені довелося поставити собі на комп'ютер XAMPP та Netbeans.
Це була для мене важка справа, бо по-перше, я не PHP-програміст, а по-друге, на моему девелоперскому комп'ютері вже працювали десяток чи більше різноманітних версій PHP, MySQL, Apache. Вони займали усі більш-менш стандартні порти та каталоги для цієї роботи. Але мені вдалось налаштувати собі середовище PHP-програміста
та-таки розпочати отладку цієї проги:
3. Об'єктна membership-надбудова.
Наступна задачка була у мене для того ж самого замовника. Вона була, мабуть, найпростішою з цього пакета, що я описую, бо не було потреби змінювати якось своє середовище роботи, це була задачка на ASP.NET MVC. Замовник забажав не тільки готові DLL, але й source code на C#, тому мені довелося перетранслювати увесь свій VB-код на С#-код. На першому скріні якісь табли до цієї задачки, на другому - мій оригінальний код, на останньому скрині - юніт тести.
4. Сайт з peer-to-peer video-комунікаціями.
Далі я виконав дуже цікаву роботу. Вона стосується обміну даними peer-to-peer. Що сталося з самим сайтом, я не знаю, я його віддав замовнику, у мене залишився перший тест цього двигуна, сподіваюсь він ще працює :
- http://rozzoil.vb-net.com/Client.aspx
- http://rozzoil.vb-net.com/Clerk.aspx
- http://rozzoil.vb-net.com/Star.aspx
Це дуже простий сайт, але він має под собою достатньо могутню теорію. Починаючи з основ, звичайно ми маємо справу з сервером та браузером, тобто всі комуникації (навіть між кліентами), виконуються за допомогою сервера та через сервер. Але існує і інша можливість, працювати без сервера, як TORRENT, чи SKYPE - тобто кліенти спілкуються між собою напрямки. Взагалі, звичайно так працюють FLEX та JAVA. Але і у JAVASCRIPT таке теж можливо. Тут взагалі багато цікавих питань, починаючи з можливості кросдоменного постбека на JavaScript у різноманітних середовищах:
Щоб зрозуміти щю проблему, яку можно вирішити або можливостями браузера або можливостями JavaScript - прочитайте ось цю сторінку - Cross-Domain Browser Window Messaging with HTML5 and Javascript.
Але кросдомений постбек на JavaScript- це лише один з можливих варіантів peer-to-peer комунікацій. Більш поширені варіанта - Flex та JAVA. Взагалі на Flex я цю можливість використовував у декількох проєктах.
На цих можливостях побудовані два цікавих публічних сервіса.
Перший сервіс мені подобаться більше, але він не дозволяє робити великі кімнати для спілкування peer-to-peer. А ось appear.in дозволяє це зробити, тому у цьому проєкті я використав саме його. Існують і інші варіанти peer-to-peer комунікацій, якийсь перелік їх є ось на цій сторінці, але ж всі ці peer-to-peer комунікації не виконуються безпосередньо з браузера. Також існує багато суто апаратних рішень для виконання peer-to-peer комунікацій, наприклад на обладнанні CISCO.
Отже у цьому проєкті я побудував сістему комунікацій на сервісі appear.in. Кліент мав можливість зробити переговорну кімнату з унікальним ID. Десь на сторінці розташовано літерал:
<asp:Literal ID="FrameLiteral1" runat="server"></asp:Literal>
І на сторінці утворюється унікальний кабінет:
Dim NewSessionID As Guid = SetSessionID(Video1.CommandName) FrameLiteral1.Text = "<iframe src='https://appear.in/" & NewSessionID.ToString & "' width='800' height='640' frameborder='0'></iframe>"
Спочатку у цей кабінет заходить "секретар", розмовляє с клієнтом, можливо перевіряє оплату, а потім запрошує до цього кабінету "зірку", спілкування з якою заказав кліент. Схема достатньо універсальна і має багато практичних застосувань.
5. Десктопна прога автоматизації роботи фірми.
Наступна прога, яку я зробив (exhibition) - wе десктопна прога роботи с базами даних, призначення цієї проги - планування виставок у виставкових центрах для різних фірм. Має багато різних особливостей, наприклад графікі обзвону кліентів.
Усі сіточні формицієї проги я побудував однаково, десь приблизно по технології, яку я описав тут - Проги під заказ і проги по натхненню. Найбільша проблема цього проєкту була в тому, що замовник передал мені дані у такому форматі, що я витратив чимало часу, поки зумів прочитати ці дані та трансформувати їх у MS SQL.
6. Розсилка SMS за допомогою GSM-передавачів GoIP та за допомогою интернет-комунікацій (WhatsApp на емуляторі Android).
У цьому проєкті я працював не тільки як програміст, а і як менеджер проєкту. Тому і розповідь про цей проєкт буде трошки глобальніше, ніж звичайно. Цей дуже цікавий проєкт занурив мене у дуже цікавий бізнес розсилки SMS. Існує багато сайтів (наприклад https://sms-reg.com/, http://virtualsim.net/index.php?l=ru, http://onlinesim.ru/, http://sms-activate.ru, https://smslike.ru/), які надають ці послуги для різноманітних цілій - починаючи з простої розсилки реклами до послуг реєстрації телефона у на різноманітних сайтах.
Взагалі, у цьому бізнесі існує два протилежних підхіда. Перший підхід - це використання GSM-шлюза с змінним IMEA - http://www.dbltek.com/pdf/GoIP%20Series%20User%20Manual%20V1.4B.pdf - а його змінювати потрібно, бо телекомуникаційні компанії вважають, що це їх власний бізнес і намагаються видавити усі інші компанії з цього бізнесу. Наприклад, ось повідомлення, як спецслужби, виконуючи заказ телекомунікаційних компаній, розгромили подібний бізнес, та ще й звинуватили цих дрібних бізнесменів у тероризмі. Для охорони бізнесу від зажерливості телекомунікаційних компаній краще розподіляти головні компоненти бізнесу по різним країнам.
Щоб рухатися таким шляхом потрібно по-перше купити передавач, тобто мобільник, восьмиканальний коштує $600 http://www.ebay.com/ А по-друге, купити SIM-банк, наприклад на 32-SIM-ки він коштує 700 долларов, а на 128 SIM-ок вже 2000 доларів - http://www.ebay.com/. А далі, якщо використовувати рішення GoIP, то потрібно самому робити шлюз, який буде звертатися до цього обладнання. Існує і більш дорогі та комплексні рішення, які вже мають шлюз, та апаратно прошитий Asterisk - SMSEagle NPE-9300 GSM/GPRS.
Інший напрямок дієї діяльності - це термінірованіе трафіку. Я багато разів стикався з такими Системами, ось тут, наприклад, я дещо описав про таку сістему - Elastix - сервер VOIP-телефонии.
Головне, що потрібно зрозуміти, рухаючись по першому шляху (з окремими передавачами) - що можно розсилати SMS-ки на звичайні мобільники. Можливо також мати зворотній зв'язок від абонентів з звичайними мобільниками. Існує і протилежний шлях - чисто інтернет-коммунікації, без використання GSM та радіо-єфіру взагалі. Таким чином можливо зв'язатися лише з сучасними комунікаторами, які тримають зв'язок по 3G/4G, або, навіть зі стаціонарними комп'ютерами. Основа такої комунікації - WhatsApp. У цьому бізнесі теж присутні SIM-банкі, але (оскільки це звичайні інтернет-комунікації), то працюють з ними зі звичайного комп'ютера, який і посилає у інтернет-мережу пакети для клієнтів.
Якщо рухатися по шляху інтернет-комунікацій, то можна звернутися до послуг сторонніх сайтів типу WhatsApp Bulk Sender, або побудувати сістему самостійно. Якщо будувати самостійно, то WhatsApp - то перше, що потрібно поставити собі на комп'ютер. Він ставиться на ємулятор системи Andriod, тобто це звичайний код операційної системи Андроід, який відтрансльований для процесора X86, та запущена ця Система на окремої віртуалці на звичайному комп'ютері. Існує готовий повний пакет https://www.genymotion.com/, який має в собі і сконфігурований VirtualBox і Андроід. Для одного ємулятора ця Система безкоштовна. Ця Система вже має добре сконфігурований VirtualBox, у якому лише потрібно налаштувати IP-адресу - і до того віртуального Андроіда вже можливо конектитись.
Наступна задача - встановити на наш віртуальний Андроід Google Play service (щоб з нього встановити WhatsApp). Для цього з одного боку на локальний комп'ютер потрібно на локальний комп'ютер встановити ADB, за допомогою якого можна керувати віртуальним приладом на Andriod. У даному випадку я також встановив Google USB Driver, який потрібен для роботи ADB.
А з іншого боку на наш віртуальний Андроід потрібно встановити ARM-транслятор, який дозволить виконуватися на машинах X86 оригінальному коду для мобільних приладів.
Потім потрібно встановити на Андроід Phone ID changer та ще декілька програм, не буду все це перераховувати до кінця, покажу на іншому комп'ютері вже процесс отладки софта, який я написав як заплатку до QueenSoft.Vn.AutoWART, щоб не звертатися до стороннього сервісу за каналами до SIM-карт, а звертатися до власного SIM-банку.
7. Проєкт Шаровоз.
Це був мій самій великий проєкт з описанного тут пакету дрібних проєктів, нажаль замовник за нього мені нічого не заплатив. Тому розповідаю про цей проєкт з усіма деталями, ніяких добрих побажань до людини, яку не заплатила мені гроші за місяць моєї роботи - у мене немає. Взагалі, власник цього бізнесу звільнив усіх своїх програмістів, остання більш-менш постійна людина пішла від нього геть на початку 2016-року, тому можливо, при такому відношенню до програмістів, цього проекту вже і не існує зовсім. Хоча цей інтернет-проєкт з непоганими грошима та з великою кількістю власних серверів, має 70 тысяч постійних користувачів, які перераховують цьому проєкту гроші - і можливість платити нормальні гроші програмістам принципово існує (але у власника цього бізнесу, здається, не існує бажання платити гроши навіть за добре виконану роботу).
Проєкт побудований таким чином - є центральний сервер во Франції, є велика кількість проксі-серверів, е декілька окремих доменів, які так чи інакше, через проксі-сервера, дивляться на центральний сервер во Франції. Також існує велика кількість серверів, на яких встановлені card-sharing сервери, які керуються з центрального сервера. Є також декілька платіжних шлюзів, через які можливо приймати гроші як за пакети в цілому, так і за окремі фільми. Працездатність такої великої кількості серверів контролюється системой Zabbix. Також в проекті активно використовується віртуалізація на базі XEN.
Коли я ближче познайомився з сістемою кард-шарінга, мені будо дуже цікаво, що клієнтом шарінга можуть бути не тільки класичні супутникові приймачи, але й персональні комп'ютери та різноманітні емулятори справжніх супутникових ресиверів. До речі, на сайті цього проєкту можливо отримати не тільки вся параметри всіх супутників, повні описи усіх пакетів, но і всі налаштування не тільки для класичних приймачів, але й для різноманітних емуляторів справжніх приймачів.
Але повернемося від загального опису проекту до центрально сервера, на якому мене наняли навести порядок. Коли я подивився на цей сервер, мене охопив жах:
- На сервері не існувало девелоперскої зони, тобто не було ніякого розгортання проєкту на бойову зону, потрібно було клацнути старт у студії - і саме цей сайт бачили кліенти. безпосередньо на самому WEB-сервері, знаходилось 60 проєктів, з яких для кліентів запускалось 24.
- Солушен був побудований аж з 24-х проєктів, абсолютно без якої-небудь сістеми. Тобто існували деякі окремі windows-сервіси, які щось робили, працювали окремі модулі, як поширення IIS, було також десь десяток сайтів, які до того ж якось взаємодіяли один з одним.
- Усі проєкти були побудовані на різних версіях .NET Framework.
- На цієї ж машині працював також SQL-сервер декількох несумісних між собою версій.
- І щоб ви остаточно зрозуміли цей ад - у проєкти були використані усі SQL та No-SQL сервери, що я взагали знаю - починаючи від MySQL та PostgreSQL до MongoDB.
- Була взагалі безліч якихось сторонніх приблуд, які ніколи взагалі не використовуються у ASP.NET, наприклад замість звичайного ASP.NET State Server використовувався якийсь Memcasche-Сервис з сайта PHP-шної CMS Drupal.
- А також на сервері були гори якогось непотрібного платного софта, до якого не було ключей, той софт увесь час викидував різноманітні окна і вимагав зареєструватися та заплатити гроши за його використання. Усі головні інструменти програміста, навіть сама Visual Studio 2015 була незареєстрована, я ії зареестрував вперше (на свій LiveID).
Значна частина цього програмного аду не працювала і взагалі незрозуміло що вона повинна була робити. Ось такий буває результат, коли власник бізнесу, навіть с непоганими грошима таким чином ставиться до програмістів, які тікають від нього як від беса. А між тим, це простий сайт, я взагалі спочатку йому запропонував побудувати його з нуля за декілька місяців - можливо це було навіть легче, ніж розібратися у всьому цьому пеклі. Але власник відмовився і ми зупинилися на першочергових діях, зробити три окремі машини - DEV (девлоперську), SQL (для SQL-серверу), та WEB (сайти з цієї машини бачать через інтернет кліенти). І по-друге ми домовились у першу чергу відремонтувати ти важливі сервіси, що не працювали - починаючи з сервісів роботи з грошима. До речі, і першу і другу частку домовленостей я виконав, тобто зробив три нові окремі машини та переробив наново сервіс роботи з веб-мані - але грошей я так і не отримав. Після виконання всіх робот, прямо у процесі налаштування одного з 24-х сайтів адмін просто закрив мені доступ до серверу та мене забанили у скайпі.
Нище ви бачите скрини, як я поставив три окремі машини - DEV, SQL, WEB. А також я накатів всі обновляшки на всі операціонки, сконфігурував їх правильно, установив і зареестрував VS2015, налаштував безліч сервісів, починаючи від FTP до GIT.
Ніякого бекапа даних у цьому проекті не велось взагалі ніколи, я першій, кто налаштував SQL-бекап вже на новому сервері, який сам поставив.
Коли я переніс SQL-сервер на окремий сервер, то Entity-framework відмовився працювати нормально, мені довелося зробити окрему латочку.
Ось тут вже процесс розгортання сайту на новому WEB-сервері, який я сам зробив для цього проекту. Коли я вперше переніс проєкт зі старого сервера на новий і спробував його откомпіліровати, я отримав перші 1100 помилок. Тільки на вирішення цієї проблеми (на першому скрині) я витратив декілька днів.
Я також повністю переробив сервіс роботи з вебмані-кишенею.
На протязі цієї роботи власник бізнесу прохав мене декілька разів ще вівести якісь необхідні дані з бази. Я роблю це просто, буквально в пару кліків мишкою, якщо не запаморочуватися на нових технологіях, а використовувати звичайні Linq-to-SQL та VB.NET - все ці маленькі сайтіки я теж зробив.
Багато ще чого я зробив корисного у цьому проєкті, але, як я розповідав вище, цей проєкт закінчився для мене несподівано.
8. Сайт для мобільників.
А третя прога грудня - це сайт для мобильників на класіке ASP.NET. У ньому я застосував було багато цікавих, але більш-менш стандартних рішень, і якби я мав вільний час, я б зробив кращій опис цього цікавого проєкту, але мабуть це вже не вийде, бо часу не вистачає катастрофічно. Отже, друзі, маю час лише на малесенький перелік рішень, які я застосував у цьому проєкті (для моїх старих друзів, для яких я пишу вже більше 15-ти років).
Отже, друзі, спочатку про ідею цього проєкту. У мене є чудовий проєкт https://arenda.votpusk.ru/default.aspx, але він не мав мобільної версії. Як я додав до цього проєкту мобільну версію, я зараз розповім. Більш повний опис цієї методики знаходиться ось тут - Як будуються адаптивні сайти для мобільників та десктопів на Classic ASP.NET..
По-перше я зробив приблизно ось такий класс, який перевіряє чи мобільний браузер застосовує клієнт, чи ні. Зрозуміло, що публікую тут лише загальну схему, у реальному проєкти все трохи складніше.
Цей клас відпрацьовує у дуже специфічній момент Page_PreInit, до того, як будуть взагалі працювати всі сторінки сайту. Цей клас оцінює реквест, чи є у ньому мобільна кука, чи немає, якщо куки мобільного прилада немає, то клас аналізує реквест - чи нас запрошує мобільний прилад?
Після цього клас пише куку мобильного прилада у поток браузера, якщо прилад мобільний, то змінює мастер-пейдж. І головне, далі все сторінки будуть мати свойство - чи був це реквест мобільний, чи ні. У трошки скороченому вигляди цей код виглядає ось так
1: Imports Microsoft.VisualBasic
2:
3: Public Class MobilePage
4: Inherits System.Web.UI.Page
5:
6: Property IsMobile As Boolean
7: Get
8: Return ViewState("_IsMobile")
9: End Get
10: Set(value As Boolean)
11: ViewState("_IsMobile") = value
12: End Set
13: End Property
14:
15: Private Sub Page_PreInit(sender As Object, e As System.EventArgs) Handles Me.PreInit
16: If Request.Cookies("Mobile") Is Nothing Then
17: GoTo CheckDeviceAndSetCookie
18: Else
19: If Request.Cookies("Mobile").Value = "" Then
20: CheckDeviceAndSetCookie:
21: ViewState("_IsMobile") = Is_Mobile()
22: If IsMobile Then
23: ViewState("_IsMobile") = True
24: Response.Cookies("Mobile").Value = "yes"
25: Response.Cookies("Mobile").Expires = Now.AddDays(1)
26: Response.Cookies("Mobile").Domain = "votpusk.ru"
27: Else
28: ViewState("_IsMobile") = False
29: Response.Cookies("Mobile").Value = "no"
30: Response.Cookies("Mobile").Expires = Now.AddDays(1)
31: Response.Cookies("Mobile").Domain = "votpusk.ru"
32: End If
33: Else
34: If Request.Cookies("Mobile").Value = "yes" Then
35: ViewState("_IsMobile") = True
36: Else
37: ViewState("_IsMobile") = False
38: End If
39: End If
40: '
41: If Me.IsMobile Then
42: If Not Request.RawUrl.Contains("Message.aspx") Then
43: Me.MasterPageFile = "M1_mobile.master"
44: End If
45: End If
46:
47: End If
48: End Sub
49:
50: Function Is_Mobile() As Boolean
51: Dim u As String = Request.ServerVariables("HTTP_USER_AGENT")
52: Dim b As New Regex("(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino", RegexOptions.IgnoreCase)
53: Dim v As New Regex("1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-", RegexOptions.IgnoreCase)
54: If b.Match(u).Success Or v.Match(Left(u, 4)).Success Then Return True Else Return False
55: End Function
56: End Class
Якщо сторінки мають таке свойство - запит з мобільного юнита, то вся сторінки можна переробити ось таким чином.
Приблизно таким же чином можна було б переробити і код сторінок, але тут ставимо кому.
Page-Load можливо переробити так, але що робити з контролами? Вони ж усі трошки різні у мобільному та немобільному варіанті.
Тому я зробив базові класи для мобільних та немобільних приладів.
Таким чином всі звичайні процедури біндінга контролів мені вдалося переробити на Дженеріках, тобто саме так я зберіг увесь раніше побудований код та уникнув дублів коду для мобільних та немобільних контролів.
Це не єдине цікаве, але досить стандартне рішення, яке я застосував у цьому сайті. Ще одне досить стандартне, але теж цікаве рішення описано ось тут SPA-page на Classic ASP.NET та jQuery.
9. Ще п'ять моїх проєктів (адміністрування серверів, конвертация даних MySQL, виготовлення відео та проект на Visual Basic 6).
Я виконав ще безліч якихось інших робот, до деяких у мене збереглось по одному скріну, до деяких - жодного скріна. На першому скріні - я добре пам'ятаю що я робив у цьому проєкті. На другому скрині - пригадую важко, якась адміністративна робота с сервером, щось там було з ILO. На третьому скрині - якась робота по конвертації бази даних з MySQL. На наступному скрині я зробив собі зведення команд FFMPEG, бо у цей період я готував дуже багато відео для своїх проєктів. А на останньому скріні - правив якусь прогу на VB6. Таких дрібних робот було просто безліч, до більшості з них не збереглось навіть скринів.
10. Два моїх останніх проєкта 2016-го року для USA (прога для медицини та торговельный робот).
Це мабуть буде виглядати як невеличкий додаток-апдейт до сторінки, але я викладу тут ще три мої проекта, які я пишу вже у грудні. Перший проєкт - це прога керування якимось медичним обладнанням, яку я написав по специфікації, прога працю. добре, заплатили всі гроші. Друга прога - це моя остання прога у 2016-му році, щось вона йде дуже туго, у мене є інші плани на кінець року, можливо вона так і не буде завершена. Це цікавий специфічній торговельний робот для біржі, але тут по-перше дуже багато речей, які до кінця не зрозуміло як зробити, тобто прога вимагає набагато більше часу і можливо ії не має сенсу закінчувати за такий час і такі гроші, а по друге у мене є багато планів на різноманітні поїздки, тому з цим проєктом є велике питання що з ним буде далі.
11. Декілька тестових проєктів для різноманітних фірм.
У цьому році я шукав гарну грошову роботу у непоганій фірмі, але так і не зайшов її поки що. Але мені кожного разу давали якийсь тестові завдання, які теж з'їли значну частку моїх сил та часу. Різноманітних тестових проєктів було чимало, і тому я написав про них детально на окремих сторінках.
12. Проги по натхненню.
Ще декілька своїх прог я описав на ціеї сторінці. Ці проги я зробив без замовлення, особисто для себе.
Маю ще багато цікавих власних задумок, але, нажаль не маю часу на їх реалізацію.